[Metrics UI] Add AWS Metricsets to Inventory Models (#49983)

* Adding initial code for EC2

* Removing obsolute files; Adding EC2;

* Removing currentTimerange and replacing it with currentTime; Timerange will now be calcuated on the server

* Fixing AWS.s3 with Metrics Explorer

* Auto calculating timerange and interval based on metricset.period

* Adding S3 metricset

* Inital addition of RDS metrics

* Adding SQS and fixing a few things

* Fixing typescript error

* Adding RDS; Adjusting fields for S3; adding new formatter

* Return 60 seconds by detault

* Fixing types

* Removing i18n

* Fixing tests

* Fixing translations

* Fixes from merge

* Removing IDX from code not covered by #52354

* fixing tests

* Adding controls for crossliking; consolidating display name

* remove obsolete import

* Adding drop_last_bucket_support to TSVB models

* Changing type

* Fixing value per type

* remvoing obsolete translation

* Removing duplicate lines

* Removing icons from switcher

* Reducing boilerplate in Toolbar Items

* Changing file name
This commit is contained in:
Chris Cowan 2019-12-12 13:17:30 -07:00 committed by GitHub
parent 31a6b5013d
commit 617c8d589a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
113 changed files with 2860 additions and 411 deletions

View file

@ -45,6 +45,8 @@ export const DOCKER_ALLOWED_LIST = [
'docker.container.labels',
];
export const AWS_S3_ALLOWED_LIST = ['aws.s3'];
export const getAllowedListForPrefix = (prefix: string) => {
const firstPart = first(prefix.split(/\./));
const defaultAllowedList = prefix ? [...ECS_ALLOWED_LIST, prefix] : ECS_ALLOWED_LIST;
@ -55,6 +57,10 @@ export const getAllowedListForPrefix = (prefix: string) => {
return [...defaultAllowedList, ...PROMETHEUS_ALLOWED_LIST];
case 'kubernetes':
return [...defaultAllowedList, ...K8S_ALLOWED_LIST];
case 'aws':
if (prefix === 'aws.s3_daily_storage') {
return [...defaultAllowedList, ...AWS_S3_ALLOWED_LIST];
}
default:
return defaultAllowedList;
}

View file

@ -30,5 +30,9 @@ export const sharedSchema = gql`
pod
container
host
awsEC2
awsS3
awsRDS
awsSQS
}
`;

View file

@ -552,6 +552,10 @@ export enum InfraNodeType {
pod = 'pod',
container = 'container',
host = 'host',
awsEC2 = 'awsEC2',
awsS3 = 'awsS3',
awsRDS = 'awsRDS',
awsSQS = 'awsSQS',
}
export enum InfraSnapshotMetricType {
@ -562,6 +566,22 @@ export enum InfraSnapshotMetricType {
tx = 'tx',
rx = 'rx',
logRate = 'logRate',
diskIOReadBytes = 'diskIOReadBytes',
diskIOWriteBytes = 'diskIOWriteBytes',
s3TotalRequests = 's3TotalRequests',
s3NumberOfObjects = 's3NumberOfObjects',
s3BucketSize = 's3BucketSize',
s3DownloadBytes = 's3DownloadBytes',
s3UploadBytes = 's3UploadBytes',
rdsConnections = 'rdsConnections',
rdsQueriesExecuted = 'rdsQueriesExecuted',
rdsActiveTransactions = 'rdsActiveTransactions',
rdsLatency = 'rdsLatency',
sqsMessagesVisible = 'sqsOldestMessage',
sqsMessagesDelayed = 'sqsMessagesDelayed',
sqsMessagesSent = 'sqsMessagesSent',
sqsMessagesEmpty = 'sqsMessagesEmpty',
sqsOldestMessage = 'sqsOldestMessage',
}
export enum InfraMetric {
@ -602,6 +622,24 @@ export enum InfraMetric {
awsNetworkPackets = 'awsNetworkPackets',
awsDiskioBytes = 'awsDiskioBytes',
awsDiskioOps = 'awsDiskioOps',
awsEC2CpuUtilization = 'awsEC2CpuUtilization',
awsEC2DiskIOBytes = 'awsEC2DiskIOBytes',
awsEC2NetworkTraffic = 'awsEC2NetworkTraffic',
awsS3TotalRequests = 'awsS3TotalRequests',
awsS3NumberOfObjects = 'awsS3NumberOfObjects',
awsS3BucketSize = 'awsS3BucketSize',
awsS3DownloadBytes = 'awsS3DownloadBytes',
awsS3UploadBytes = 'awsS3UploadBytes',
awsRDSCpuTotal = 'awsRDSCpuTotal',
awsRDSConnections = 'awsRDSConnections',
awsRDSQueriesExecuted = 'awsRDSQueriesExecuted',
awsRDSActiveTransactions = 'awsRDSActiveTransactions',
awsRDSLatency = 'awsRDSLatency',
awsSQSMessagesVisible = 'awsSQSMessagesVisible',
awsSQSMessagesDelayed = 'awsSQSMessagesDelayed',
awsSQSMessagesSent = 'awsSQSMessagesSent',
awsSQSMessagesEmpty = 'awsSQSMessagesEmpty',
awsSQSOldestMessage = 'awsSQSOldestMessage',
custom = 'custom',
}

View file

@ -5,16 +5,11 @@
*/
import * as rt from 'io-ts';
export const InfraMetadataNodeTypeRT = rt.keyof({
host: null,
pod: null,
container: null,
});
import { ItemTypeRT } from '../../common/inventory_models/types';
export const InfraMetadataRequestRT = rt.type({
nodeId: rt.string,
nodeType: InfraMetadataNodeTypeRT,
nodeType: ItemTypeRT,
sourceId: rt.string,
});
@ -96,5 +91,3 @@ export type InfraMetadataMachine = rt.TypeOf<typeof InfraMetadataMachineRT>;
export type InfraMetadataHost = rt.TypeOf<typeof InfraMetadataHostRT>;
export type InfraMetadataOS = rt.TypeOf<typeof InfraMetadataOSRT>;
export type InfraMetadataNodeType = rt.TypeOf<typeof InfraMetadataNodeTypeRT>;

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
export const awsEC2: InventoryModel = {
id: 'awsEC2',
displayName: i18n.translate('xpack.infra.inventoryModels.awsEC2.displayName', {
defaultMessage: 'EC2 Instances',
}),
requiredModules: ['aws'],
crosslinkSupport: {
details: true,
logs: true,
apm: true,
uptime: true,
},
metrics,
fields: {
id: 'cloud.instance.id',
name: 'cloud.instance.name',
ip: 'aws.ec2.instance.public.ip',
},
requiredMetrics: ['awsEC2CpuUtilization', 'awsEC2NetworkTraffic', 'awsEC2DiskIOBytes'],
};

View file

@ -0,0 +1,116 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
import { Section } from '../../../public/pages/metrics/components/section';
import { SubSection } from '../../../public/pages/metrics/components/sub_section';
import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
import { withTheme } from '../../../../../common/eui_styled_components';
export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
<React.Fragment>
<Section
navLabel="AWS EC2"
sectionLabel={i18n.translate(
'xpack.infra.metricDetailPage.ec2MetricsLayout.overviewSection.sectionLabel',
{
defaultMessage: 'Aws EC2 Overview',
}
)}
metrics={metrics}
>
<SubSection
id="awsEC2CpuUtilization"
label={i18n.translate(
'xpack.infra.metricDetailPage.ec2MetricsLayout.cpuUsageSection.sectionLabel',
{
defaultMessage: 'CPU Usage',
}
)}
>
<ChartSectionVis
stacked={true}
type="area"
formatter="percent"
seriesOverrides={{
total: { color: theme.eui.euiColorVis1 },
}}
/>
</SubSection>
<SubSection
id="awsEC2NetworkTraffic"
label={i18n.translate(
'xpack.infra.metricDetailPage.ec2MetricsLayout.networkTrafficSection.sectionLabel',
{
defaultMessage: 'Network Traffic',
}
)}
>
<ChartSectionVis
formatter="bits"
formatterTemplate="{{value}}/s"
type="area"
seriesOverrides={{
rx: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.networkTrafficSection.networkRxRateSeriesLabel',
{
defaultMessage: 'in',
}
),
},
tx: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.networkTrafficSection.networkTxRateSeriesLabel',
{
defaultMessage: 'out',
}
),
},
}}
/>
</SubSection>
<SubSection
id="awsEC2DiskIOBytes"
label={i18n.translate(
'xpack.infra.metricDetailPage.ec2MetricsLayout.diskIOBytesSection.sectionLabel',
{
defaultMessage: 'Disk IO (Bytes)',
}
)}
>
<ChartSectionVis
formatter="bytes"
formatterTemplate="{{value}}/s"
type="area"
seriesOverrides={{
write: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.ec2MetricsLayout.diskIOBytesSection.writeLabel',
{
defaultMessage: 'writes',
}
),
},
read: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.ec2MetricsLayout.diskIOBytesSection.readLabel',
{
defaultMessage: 'reads',
}
),
},
}}
/>
</SubSection>
</Section>
</React.Fragment>
));

View file

@ -0,0 +1,28 @@
/*
* 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 { cpu } from './snapshot/cpu';
import { rx } from './snapshot/rx';
import { tx } from './snapshot/tx';
import { diskIOReadBytes } from './snapshot/disk_io_read_bytes';
import { diskIOWriteBytes } from './snapshot/disk_io_write_bytes';
import { awsEC2CpuUtilization } from './tsvb/aws_ec2_cpu_utilization';
import { awsEC2NetworkTraffic } from './tsvb/aws_ec2_network_traffic';
import { awsEC2DiskIOBytes } from './tsvb/aws_ec2_diskio_bytes';
import { InventoryMetrics } from '../../types';
export const metrics: InventoryMetrics = {
tsvb: {
awsEC2CpuUtilization,
awsEC2NetworkTraffic,
awsEC2DiskIOBytes,
},
snapshot: { cpu, rx, tx, diskIOReadBytes, diskIOWriteBytes },
defaultSnapshot: 'cpu',
defaultTimeRangeInSeconds: 14400, // 4 hours
};

View file

@ -0,0 +1,27 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const cpu: SnapshotModel = {
cpu_avg: {
avg: {
field: 'aws.ec2.cpu.total.pct',
},
},
cpu: {
bucket_script: {
buckets_path: {
cpu: 'cpu_avg',
},
script: {
source: 'params.cpu / 100',
lang: 'painless',
},
gap_policy: 'skip',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const diskIOReadBytes: SnapshotModel = {
diskIOReadBytes: {
avg: {
field: 'aws.ec2.diskio.read.bytes_per_sec',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const diskIOWriteBytes: SnapshotModel = {
diskIOWriteBytes: {
avg: {
field: 'aws.ec2.diskio.write.bytes_per_sec',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const rx: SnapshotModel = {
rx: {
avg: {
field: 'aws.ec2.network.in.bytes_per_sec',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const tx: SnapshotModel = {
tx: {
avg: {
field: 'aws.ec2.network.in.bytes_per_sec',
},
},
};

View file

@ -0,0 +1,37 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsEC2CpuUtilization = createTSVBModel(
'awsEC2CpuUtilization',
['aws.ec2'],
[
{
id: 'total',
split_mode: 'everything',
metrics: [
{
field: 'aws.ec2.cpu.total.pct',
id: 'avg-cpu',
type: 'avg',
},
{
id: 'convert-to-percent',
script: 'params.avg / 100',
type: 'calculation',
variables: [
{
field: 'avg-cpu',
id: 'var-avg',
name: 'avg',
},
],
},
],
},
]
);

View file

@ -0,0 +1,41 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsEC2DiskIOBytes = createTSVBModel(
'awsEC2DiskIOBytes',
['aws.ec2'],
[
{
id: 'write',
split_mode: 'everything',
metrics: [
{
field: 'aws.ec2.diskio.write.bytes_per_sec',
id: 'avg-write',
type: 'avg',
},
],
},
{
id: 'read',
split_mode: 'everything',
metrics: [
{
field: 'aws.ec2.diskio.read.bytes_per_sec',
id: 'avg-read',
type: 'avg',
},
{
id: 'calculation-rate',
type: 'calculation',
variables: [{ id: 'rate-var', name: 'rate', field: 'avg-read' }],
script: 'params.rate * -1',
},
],
},
]
);

View file

@ -0,0 +1,41 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsEC2NetworkTraffic = createTSVBModel(
'awsEC2NetworkTraffic',
['aws.ec2'],
[
{
id: 'tx',
split_mode: 'everything',
metrics: [
{
field: 'aws.ec2.network.out.bytes_per_sec',
id: 'avg-tx',
type: 'avg',
},
],
},
{
id: 'rx',
split_mode: 'everything',
metrics: [
{
field: 'aws.ec2.network.in.bytes_per_sec',
id: 'avg-rx',
type: 'avg',
},
{
id: 'calculation-rate',
type: 'calculation',
variables: [{ id: 'rate-var', name: 'rate', field: 'avg-rx' }],
script: 'params.rate * -1',
},
],
},
]
);

View file

@ -0,0 +1,33 @@
/*
* 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 { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
import { InfraSnapshotMetricType } from '../../graphql/types';
export const AwsEC2ToolbarItems = (props: ToolbarProps) => {
const metricTypes = [
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
InfraSnapshotMetricType.diskIOReadBytes,
InfraSnapshotMetricType.diskIOWriteBytes,
];
const groupByFields = [
'cloud.availability_zone',
'cloud.machine.type',
'aws.ec2.instance.image.id',
'aws.ec2.instance.state.name',
];
return (
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
export const awsRDS: InventoryModel = {
id: 'awsRDS',
displayName: i18n.translate('xpack.infra.inventoryModels.awsRDS.displayName', {
defaultMessage: 'RDS Databases',
}),
requiredModules: ['aws'],
crosslinkSupport: {
details: true,
logs: true,
apm: false,
uptime: false,
},
metrics,
fields: {
id: 'aws.rds.db_instance.arn',
name: 'aws.rds.db_instance.identifier',
},
requiredMetrics: [
'awsRDSCpuTotal',
'awsRDSConnections',
'awsRDSQueriesExecuted',
'awsRDSActiveTransactions',
'awsRDSLatency',
],
};

View file

@ -0,0 +1,180 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
import { Section } from '../../../public/pages/metrics/components/section';
import { SubSection } from '../../../public/pages/metrics/components/sub_section';
import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
import { withTheme } from '../../../../../common/eui_styled_components';
export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
<React.Fragment>
<Section
navLabel="AWS RDS"
sectionLabel={i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.overviewSection.sectionLabel',
{
defaultMessage: 'Aws RDS Overview',
}
)}
metrics={metrics}
>
<SubSection
id="awsRDSCpuTotal"
label={i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.cpuTotal.sectionLabel',
{
defaultMessage: 'Total CPU Usage',
}
)}
>
<ChartSectionVis
type="area"
formatter="percent"
seriesOverrides={{
cpu: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.cpuTotal.chartLabel',
{ defaultMessage: 'Total' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsRDSConnections"
label={i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.connections.sectionLabel',
{
defaultMessage: 'Connections',
}
)}
>
<ChartSectionVis
type="bar"
formatter="number"
seriesOverrides={{
connections: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.connections.chartLabel',
{ defaultMessage: 'Connections' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsRDSQueriesExecuted"
label={i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.queriesExecuted.sectionLabel',
{
defaultMessage: 'Queries Executed',
}
)}
>
<ChartSectionVis
type="bar"
formatter="number"
seriesOverrides={{
queries: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.queriesExecuted.chartLabel',
{ defaultMessage: 'Queries' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsRDSActiveTransactions"
label={i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.activeTransactions.sectionLabel',
{
defaultMessage: 'Transactions',
}
)}
>
<ChartSectionVis
type="bar"
formatter="number"
seriesOverrides={{
active: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.active.chartLabel',
{ defaultMessage: 'Active' }
),
},
blocked: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.blocked.chartLabel',
{ defaultMessage: 'Blocked' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsRDSLatency"
label={i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.latency.sectionLabel',
{
defaultMessage: 'Latency',
}
)}
>
<ChartSectionVis
type="bar"
stacked={true}
formatter="highPercision"
formatterTemplate={'{{value}} ms'}
seriesOverrides={{
read: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.latency.read.chartLabel',
{ defaultMessage: 'Read' }
),
},
write: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.latency.write.chartLabel',
{ defaultMessage: 'Write' }
),
},
insert: {
color: theme.eui.euiColorVis0,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.latency.insert.chartLabel',
{ defaultMessage: 'Insert' }
),
},
update: {
color: theme.eui.euiColorVis7,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.latency.update.chartLabel',
{ defaultMessage: 'Update' }
),
},
commit: {
color: theme.eui.euiColorVis3,
name: i18n.translate(
'xpack.infra.metricDetailPage.rdsMetricsLayout.latency.commit.chartLabel',
{ defaultMessage: 'Commit' }
),
},
}}
/>
</SubSection>
</Section>
</React.Fragment>
));

View file

@ -0,0 +1,38 @@
/*
* 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 { InventoryMetrics } from '../../types';
import { cpu } from './snapshot/cpu';
import { rdsLatency } from './snapshot/rds_latency';
import { rdsConnections } from './snapshot/rds_connections';
import { rdsQueriesExecuted } from './snapshot/rds_queries_executed';
import { rdsActiveTransactions } from './snapshot/rds_active_transactions';
import { awsRDSLatency } from './tsvb/aws_rds_latency';
import { awsRDSConnections } from './tsvb/aws_rds_connections';
import { awsRDSCpuTotal } from './tsvb/aws_rds_cpu_total';
import { awsRDSQueriesExecuted } from './tsvb/aws_rds_queries_executed';
import { awsRDSActiveTransactions } from './tsvb/aws_rds_active_transactions';
export const metrics: InventoryMetrics = {
tsvb: {
awsRDSLatency,
awsRDSConnections,
awsRDSCpuTotal,
awsRDSQueriesExecuted,
awsRDSActiveTransactions,
},
snapshot: {
cpu,
rdsLatency,
rdsConnections,
rdsQueriesExecuted,
rdsActiveTransactions,
},
defaultSnapshot: 'cpu',
defaultTimeRangeInSeconds: 14400, // 4 hours
};

View file

@ -0,0 +1,27 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const cpu: SnapshotModel = {
cpu_avg: {
avg: {
field: 'aws.rds.cpu.total.pct',
},
},
cpu: {
bucket_script: {
buckets_path: {
cpu: 'cpu_avg',
},
script: {
source: 'params.cpu / 100',
lang: 'painless',
},
gap_policy: 'skip',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const rdsActiveTransactions: SnapshotModel = {
rdsActiveTransactions: {
avg: {
field: 'aws.rds.transactions.active',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const rdsConnections: SnapshotModel = {
rdsConnections: {
avg: {
field: 'aws.rds.database_connections',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const rdsLatency: SnapshotModel = {
rdsLatency: {
avg: {
field: 'aws.rds.latency.dml',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const rdsQueriesExecuted: SnapshotModel = {
rdsQueriesExecuted: {
avg: {
field: 'aws.rds.queries',
},
},
};

View file

@ -0,0 +1,36 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsRDSActiveTransactions = createTSVBModel(
'awsRDSActiveTransactions',
['aws.rds'],
[
{
id: 'active',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.transactions.active',
id: 'avg',
type: 'avg',
},
],
},
{
id: 'blocked',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.transactions.blocked',
id: 'avg',
type: 'avg',
},
],
},
]
);

View file

@ -0,0 +1,25 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsRDSConnections = createTSVBModel(
'awsRDSConnections',
['aws.rds'],
[
{
id: 'connections',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.database_connections',
id: 'avg-conns',
type: 'avg',
},
],
},
]
);

View file

@ -0,0 +1,37 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsRDSCpuTotal = createTSVBModel(
'awsRDSCpuTotal',
['aws.rds'],
[
{
id: 'cpu',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.cpu.total.pct',
id: 'avg-cpu',
type: 'avg',
},
{
id: 'convert-to-percent',
script: 'params.avg / 100',
type: 'calculation',
variables: [
{
field: 'avg-cpu',
id: 'var-avg',
name: 'avg',
},
],
},
],
},
]
);

View file

@ -0,0 +1,68 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsRDSLatency = createTSVBModel(
'awsRDSLatency',
['aws.rds'],
[
{
id: 'read',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.latency.read',
id: 'avg',
type: 'avg',
},
],
},
{
id: 'write',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.latency.write',
id: 'avg',
type: 'avg',
},
],
},
{
id: 'insert',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.latency.insert',
id: 'avg',
type: 'avg',
},
],
},
{
id: 'update',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.latency.update',
id: 'avg',
type: 'avg',
},
],
},
{
id: 'commit',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.latency.commit',
id: 'avg',
type: 'avg',
},
],
},
]
);

View file

@ -0,0 +1,24 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsRDSQueriesExecuted = createTSVBModel(
'awsRDSQueriesExecuted',
['aws.rds'],
[
{
id: 'queries',
split_mode: 'everything',
metrics: [
{
field: 'aws.rds.queries',
id: 'avg-queries',
type: 'avg',
},
],
},
]
);

View file

@ -0,0 +1,32 @@
/*
* 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 { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { InfraSnapshotMetricType } from '../../../public/graphql/types';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
export const AwsRDSToolbarItems = (props: ToolbarProps) => {
const metricTypes = [
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.rdsConnections,
InfraSnapshotMetricType.rdsQueriesExecuted,
InfraSnapshotMetricType.rdsActiveTransactions,
InfraSnapshotMetricType.rdsLatency,
];
const groupByFields = [
'cloud.availability_zone',
'aws.rds.db_instance.class',
'aws.rds.db_instance.status',
];
return (
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
export const awsS3: InventoryModel = {
id: 'awsS3',
displayName: i18n.translate('xpack.infra.inventoryModels.awsS3.displayName', {
defaultMessage: 'S3 Buckets',
}),
requiredModules: ['aws'],
crosslinkSupport: {
details: true,
logs: true,
apm: false,
uptime: false,
},
metrics,
fields: {
id: 'aws.s3.bucket.name',
name: 'aws.s3.bucket.name',
},
requiredMetrics: [
'awsS3BucketSize',
'awsS3NumberOfObjects',
'awsS3TotalRequests',
'awsS3DownloadBytes',
'awsS3UploadBytes',
],
};

View file

@ -0,0 +1,143 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
import { Section } from '../../../public/pages/metrics/components/section';
import { SubSection } from '../../../public/pages/metrics/components/sub_section';
import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
import { withTheme } from '../../../../../common/eui_styled_components';
export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
<React.Fragment>
<Section
navLabel="AWS S3"
sectionLabel={i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.overviewSection.sectionLabel',
{
defaultMessage: 'Aws S3 Overview',
}
)}
metrics={metrics}
>
<SubSection
id="awsS3BucketSize"
label={i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.bucketSize.sectionLabel',
{
defaultMessage: 'Bucket Size',
}
)}
>
<ChartSectionVis
type="bar"
formatter="bytes"
seriesOverrides={{
bytes: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.bucketSize.chartLabel',
{ defaultMessage: 'Total Bytes' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsS3NumberOfObjects"
label={i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.numberOfObjects.sectionLabel',
{
defaultMessage: 'Number of Objects',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
objects: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.numberOfObjects.chartLabel',
{ defaultMessage: 'Objects' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsS3TotalRequests"
label={i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.totalRequests.sectionLabel',
{
defaultMessage: 'Total Requests',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
total: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.totalRequests.chartLabel',
{ defaultMessage: 'Requests' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsS3DownloadBytes"
label={i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.downloadBytes.sectionLabel',
{
defaultMessage: 'Downloaded Bytes',
}
)}
>
<ChartSectionVis
type="bar"
formatter="bytes"
seriesOverrides={{
bytes: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.downloadBytes.chartLabel',
{ defaultMessage: 'Bytes' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsS3UploadBytes"
label={i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.uploadBytes.sectionLabel',
{
defaultMessage: 'Uploaded Bytes',
}
)}
>
<ChartSectionVis
type="bar"
formatter="bytes"
seriesOverrides={{
bytes: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.s3MetricsLayout.uploadBytes.chartLabel',
{ defaultMessage: 'Bytes' }
),
},
}}
/>
</SubSection>
</Section>
</React.Fragment>
));

View file

@ -0,0 +1,38 @@
/*
* 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 { InventoryMetrics } from '../../types';
import { awsS3BucketSize } from './tsvb/aws_s3_bucket_size';
import { awsS3TotalRequests } from './tsvb/aws_s3_total_requests';
import { awsS3NumberOfObjects } from './tsvb/aws_s3_number_of_objects';
import { awsS3DownloadBytes } from './tsvb/aws_s3_download_bytes';
import { awsS3UploadBytes } from './tsvb/aws_s3_upload_bytes';
import { s3BucketSize } from './snapshot/s3_bucket_size';
import { s3TotalRequests } from './snapshot/s3_total_requests';
import { s3NumberOfObjects } from './snapshot/s3_number_of_objects';
import { s3DownloadBytes } from './snapshot/s3_download_bytes';
import { s3UploadBytes } from './snapshot/s3_upload_bytes';
export const metrics: InventoryMetrics = {
tsvb: {
awsS3BucketSize,
awsS3TotalRequests,
awsS3NumberOfObjects,
awsS3DownloadBytes,
awsS3UploadBytes,
},
snapshot: {
s3BucketSize,
s3NumberOfObjects,
s3TotalRequests,
s3UploadBytes,
s3DownloadBytes,
},
defaultSnapshot: 's3BucketSize',
defaultTimeRangeInSeconds: 86400 * 7, // 7 days
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const s3BucketSize: SnapshotModel = {
s3BucketSize: {
max: {
field: 'aws.s3_daily_storage.bucket.size.bytes',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const s3DownloadBytes: SnapshotModel = {
s3DownloadBytes: {
max: {
field: 'aws.s3_request.downloaded.bytes',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const s3NumberOfObjects: SnapshotModel = {
s3NumberOfObjects: {
max: {
field: 'aws.s3_daily_storage.number_of_objects',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const s3TotalRequests: SnapshotModel = {
s3TotalRequests: {
max: {
field: 'aws.s3_request.requests.total',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const s3UploadBytes: SnapshotModel = {
s3UploadBytes: {
max: {
field: 'aws.s3_request.uploaded.bytes',
},
},
};

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsS3BucketSize = createTSVBModel(
'awsS3BucketSize',
['aws.s3_daily_storage'],
[
{
id: 'bytes',
split_mode: 'everything',
metrics: [
{
field: 'aws.s3_daily_storage.bucket.size.bytes',
id: 'max-bytes',
type: 'max',
},
],
},
],
'>=86400s',
false
);

View file

@ -0,0 +1,25 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsS3DownloadBytes = createTSVBModel(
'awsS3DownloadBytes',
['aws.s3_request'],
[
{
id: 'bytes',
split_mode: 'everything',
metrics: [
{
field: 'aws.s3_request.downloaded.bytes',
id: 'max-bytes',
type: 'max',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsS3NumberOfObjects = createTSVBModel(
'awsS3NumberOfObjects',
['aws.s3_daily_storage'],
[
{
id: 'objects',
split_mode: 'everything',
metrics: [
{
field: 'aws.s3_daily_storage.number_of_objects',
id: 'max-size',
type: 'max',
},
],
},
],
'>=86400s',
false
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsS3TotalRequests = createTSVBModel(
'awsS3TotalRequests',
['aws.s3_request'],
[
{
id: 'total',
split_mode: 'everything',
metrics: [
{
field: 'aws.s3_request.requests.total',
id: 'max-size',
type: 'max',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsS3UploadBytes = createTSVBModel(
'awsS3UploadBytes',
['aws.s3_request'],
[
{
id: 'bytes',
split_mode: 'everything',
metrics: [
{
field: 'aws.s3_request.uploaded.bytes',
id: 'max-bytes',
type: 'max',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,28 @@
/*
* 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 { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { InfraSnapshotMetricType } from '../../../public/graphql/types';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
export const AwsS3ToolbarItems = (props: ToolbarProps) => {
const metricTypes = [
InfraSnapshotMetricType.s3BucketSize,
InfraSnapshotMetricType.s3NumberOfObjects,
InfraSnapshotMetricType.s3TotalRequests,
InfraSnapshotMetricType.s3DownloadBytes,
InfraSnapshotMetricType.s3UploadBytes,
];
const groupByFields = ['cloud.region'];
return (
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
export const awsSQS: InventoryModel = {
id: 'awsSQS',
displayName: i18n.translate('xpack.infra.inventoryModels.awsSQS.displayName', {
defaultMessage: 'SQS Queues',
}),
requiredModules: ['aws'],
crosslinkSupport: {
details: true,
logs: true,
apm: false,
uptime: false,
},
metrics,
fields: {
id: 'aws.sqs.queue.name',
name: 'aws.sqs.queue.name',
},
requiredMetrics: [
'awsSQSMessagesVisible',
'awsSQSMessagesDelayed',
'awsSQSMessagesSent',
'awsSQSMessagesEmpty',
'awsSQSOldestMessage',
],
};

View file

@ -0,0 +1,143 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
import { Section } from '../../../public/pages/metrics/components/section';
import { SubSection } from '../../../public/pages/metrics/components/sub_section';
import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
import { withTheme } from '../../../../../common/eui_styled_components';
export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
<React.Fragment>
<Section
navLabel="AWS SQS"
sectionLabel={i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.overviewSection.sectionLabel',
{
defaultMessage: 'Aws SQS Overview',
}
)}
metrics={metrics}
>
<SubSection
id="awsSQSMessagesVisible"
label={i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesVisible.sectionLabel',
{
defaultMessage: 'Messages Available',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
visible: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesVisible.chartLabel',
{ defaultMessage: 'Available' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsSQSMessagesDelayed"
label={i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesDelayed.sectionLabel',
{
defaultMessage: 'Messages Delayed',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
delayed: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesDelayed.chartLabel',
{ defaultMessage: 'Delayed' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsSQSMessagesSent"
label={i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesSent.sectionLabel',
{
defaultMessage: 'Messages Added',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
sent: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesSent.chartLabel',
{ defaultMessage: 'Added' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsSQSMessagesEmpty"
label={i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesEmpty.sectionLabel',
{
defaultMessage: 'Messages Empty',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
sent: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.messagesEmpty.chartLabel',
{ defaultMessage: 'Empty' }
),
},
}}
/>
</SubSection>
<SubSection
id="awsSQSOldestMessage"
label={i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.oldestMessage.sectionLabel',
{
defaultMessage: 'Oldest Message',
}
)}
>
<ChartSectionVis
type="bar"
formatter="abbreviatedNumber"
seriesOverrides={{
oldest: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.sqsMetricsLayout.oldestMessage.chartLabel',
{ defaultMessage: 'Age' }
),
},
}}
/>
</SubSection>
</Section>
</React.Fragment>
));

View file

@ -0,0 +1,38 @@
/*
* 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 { InventoryMetrics } from '../../types';
import { sqsMessagesVisible } from './snapshot/sqs_messages_visible';
import { sqsMessagesDelayed } from './snapshot/sqs_messages_delayed';
import { sqsMessagesEmpty } from './snapshot/sqs_messages_empty';
import { sqsMessagesSent } from './snapshot/sqs_messages_sent';
import { sqsOldestMessage } from './snapshot/sqs_oldest_message';
import { awsSQSMessagesVisible } from './tsvb/aws_sqs_messages_visible';
import { awsSQSMessagesDelayed } from './tsvb/aws_sqs_messages_delayed';
import { awsSQSMessagesSent } from './tsvb/aws_sqs_messages_sent';
import { awsSQSMessagesEmpty } from './tsvb/aws_sqs_messages_empty';
import { awsSQSOldestMessage } from './tsvb/aws_sqs_oldest_message';
export const metrics: InventoryMetrics = {
tsvb: {
awsSQSMessagesVisible,
awsSQSMessagesDelayed,
awsSQSMessagesSent,
awsSQSMessagesEmpty,
awsSQSOldestMessage,
},
snapshot: {
sqsMessagesVisible,
sqsMessagesDelayed,
sqsMessagesEmpty,
sqsMessagesSent,
sqsOldestMessage,
},
defaultSnapshot: 'sqsMessagesVisible',
defaultTimeRangeInSeconds: 14400, // 4 hours
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const sqsMessagesDelayed: SnapshotModel = {
sqsMessagesDelayed: {
max: {
field: 'aws.sqs.messages.delayed',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const sqsMessagesEmpty: SnapshotModel = {
sqsMessagesEmpty: {
max: {
field: 'aws.sqs.messages.not_visible',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const sqsMessagesSent: SnapshotModel = {
sqsMessagesSent: {
max: {
field: 'aws.sqs.messages.sent',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const sqsMessagesVisible: SnapshotModel = {
sqsMessagesVisible: {
avg: {
field: 'aws.sqs.messages.visible',
},
},
};

View file

@ -0,0 +1,15 @@
/*
* 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 { SnapshotModel } from '../../../types';
export const sqsOldestMessage: SnapshotModel = {
sqsOldestMessage: {
max: {
field: 'aws.sqs.oldest_message_age.sec',
},
},
};

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsSQSMessagesDelayed = createTSVBModel(
'awsSQSMessagesDelayed',
['aws.sqs'],
[
{
id: 'delayed',
split_mode: 'everything',
metrics: [
{
field: 'aws.sqs.messages.delayed',
id: 'avg-delayed',
type: 'avg',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsSQSMessagesEmpty = createTSVBModel(
'awsSQSMessagesEmpty',
['aws.sqs'],
[
{
id: 'empty',
split_mode: 'everything',
metrics: [
{
field: 'aws.sqs.messages.not_visible',
id: 'avg-empty',
type: 'avg',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsSQSMessagesSent = createTSVBModel(
'awsSQSMessagesSent',
['aws.sqs'],
[
{
id: 'sent',
split_mode: 'everything',
metrics: [
{
field: 'aws.sqs.messages.sent',
id: 'avg-sent',
type: 'avg',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsSQSMessagesVisible = createTSVBModel(
'awsSQSMessagesVisible',
['aws.sqs'],
[
{
id: 'visible',
split_mode: 'everything',
metrics: [
{
field: 'aws.sqs.messages.visible',
id: 'avg-visible',
type: 'avg',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,26 @@
/*
* 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 { createTSVBModel } from '../../../create_tsvb_model';
export const awsSQSOldestMessage = createTSVBModel(
'awsSQSOldestMessage',
['aws.sqs'],
[
{
id: 'oldest',
split_mode: 'everything',
metrics: [
{
field: 'aws.sqs.oldest_message_age.sec',
id: 'max-oldest',
type: 'max',
},
],
},
],
'>=300s'
);

View file

@ -0,0 +1,28 @@
/*
* 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 { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
import { InfraSnapshotMetricType } from '../../graphql/types';
export const AwsSQSToolbarItems = (props: ToolbarProps) => {
const metricTypes = [
InfraSnapshotMetricType.sqsMessagesVisible,
InfraSnapshotMetricType.sqsMessagesDelayed,
InfraSnapshotMetricType.sqsMessagesSent,
InfraSnapshotMetricType.sqsMessagesEmpty,
InfraSnapshotMetricType.sqsOldestMessage,
];
const groupByFields = ['cloud.region'];
return (
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -4,12 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
export const container: InventoryModel = {
id: 'container',
displayName: i18n.translate('xpack.infra.inventoryModel.container.displayName', {
defaultMessage: 'Docker Containers',
}),
requiredModules: ['docker'],
crosslinkSupport: {
details: true,
logs: true,
apm: true,
uptime: true,
},
fields: {
id: 'container.id',
name: 'container.name',
ip: 'continaer.ip_address',
},
metrics,
requiredMetrics: [
'containerOverview',

View file

@ -30,4 +30,5 @@ export const metrics: InventoryMetrics = {
},
snapshot: { cpu, memory, rx, tx },
defaultSnapshot: 'cpu',
defaultTimeRangeInSeconds: 3600, // 1 hour
};

View file

@ -4,61 +4,31 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useMemo } from 'react';
import { EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { WaffleMetricControls } from '../../../public/components/waffle/waffle_metric_controls';
import { WaffleGroupByControls } from '../../../public/components/waffle/waffle_group_by_controls';
import { InfraSnapshotMetricType } from '../../../public/graphql/types';
import {
toMetricOpt,
toGroupByOpt,
} from '../../../public/components/inventory/toolbars/toolbar_wrapper';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
import { InfraSnapshotMetricType } from '../../graphql/types';
export const ContainerToolbarItems = (props: ToolbarProps) => {
const options = useMemo(
() =>
[
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.memory,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
].map(toMetricOpt),
[]
);
const groupByOptions = useMemo(
() =>
[
'host.name',
'cloud.availability_zone',
'cloud.machine.type',
'cloud.project.id',
'cloud.provider',
'service.type',
].map(toGroupByOpt),
[]
);
const metricTypes = [
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.memory,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
];
const groupByFields = [
'host.name',
'cloud.availability_zone',
'cloud.machine.type',
'cloud.project.id',
'cloud.provider',
'service.type',
];
return (
<>
<EuiFlexItem grow={false}>
<WaffleMetricControls
metric={props.metric}
options={options}
onChange={props.changeMetric}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<WaffleGroupByControls
options={groupByOptions}
groupBy={props.groupBy}
nodeType={props.nodeType}
onChange={props.changeGroupBy}
fields={props.createDerivedIndexPattern('metrics').fields}
onChangeCustomOptions={props.changeCustomOptions}
customOptions={props.customOptions}
/>
</EuiFlexItem>
</>
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -0,0 +1,24 @@
/*
* 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 { TSVBMetricModelCreator, TSVBMetricModel, TSVBSeries, InventoryMetric } from './types';
export const createTSVBModel = (
id: InventoryMetric,
requires: string[],
series: TSVBSeries[],
interval = '>=300s',
dropLastBucket = true
): TSVBMetricModelCreator => (timeField, indexPattern): TSVBMetricModel => ({
id,
requires,
drop_last_bucket: dropLastBucket,
index_pattern: indexPattern,
interval,
time_field: timeField,
type: 'timeseries',
series,
});

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
import {
@ -13,7 +14,21 @@ import {
export const host: InventoryModel = {
id: 'host',
displayName: i18n.translate('xpack.infra.inventoryModel.host.displayName', {
defaultMessage: 'Hosts',
}),
requiredModules: ['system'],
crosslinkSupport: {
details: true,
logs: true,
apm: true,
uptime: true,
},
fields: {
id: 'host.name',
name: 'host.name',
ip: 'host.ip',
},
metrics,
requiredMetrics: [
'hostSystemOverview',

View file

@ -52,4 +52,5 @@ export const metrics: InventoryMetrics = {
},
snapshot: { count, cpu, load, logRate, memory, rx, tx },
defaultSnapshot: 'cpu',
defaultTimeRangeInSeconds: 3600, // 1 hour
};

View file

@ -4,63 +4,32 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useMemo } from 'react';
import { EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { WaffleMetricControls } from '../../../public/components/waffle/waffle_metric_controls';
import { WaffleGroupByControls } from '../../../public/components/waffle/waffle_group_by_controls';
import { InfraSnapshotMetricType } from '../../../public/graphql/types';
import {
toGroupByOpt,
toMetricOpt,
} from '../../../public/components/inventory/toolbars/toolbar_wrapper';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
import { InfraSnapshotMetricType } from '../../graphql/types';
export const HostToolbarItems = (props: ToolbarProps) => {
const metricOptions = useMemo(
() =>
[
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.memory,
InfraSnapshotMetricType.load,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
InfraSnapshotMetricType.logRate,
].map(toMetricOpt),
[]
);
const groupByOptions = useMemo(
() =>
[
'cloud.availability_zone',
'cloud.machine.type',
'cloud.project.id',
'cloud.provider',
'service.type',
].map(toGroupByOpt),
[]
);
const metricTypes = [
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.memory,
InfraSnapshotMetricType.load,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
InfraSnapshotMetricType.logRate,
];
const groupByFields = [
'cloud.availability_zone',
'cloud.machine.type',
'cloud.project.id',
'cloud.provider',
'service.type',
];
return (
<>
<EuiFlexItem grow={false}>
<WaffleMetricControls
options={metricOptions}
metric={props.metric}
onChange={props.changeMetric}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<WaffleGroupByControls
options={groupByOptions}
groupBy={props.groupBy}
nodeType={props.nodeType}
onChange={props.changeGroupBy}
fields={props.createDerivedIndexPattern('metrics').fields}
onChangeCustomOptions={props.changeCustomOptions}
customOptions={props.customOptions}
/>
</EuiFlexItem>
</>
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -7,11 +7,15 @@
import { i18n } from '@kbn/i18n';
import { host } from './host';
import { pod } from './pod';
import { awsEC2 } from './aws_ec2';
import { awsS3 } from './aws_s3';
import { awsRDS } from './aws_rds';
import { awsSQS } from './aws_sqs';
import { container } from './container';
import { InventoryItemType } from './types';
export { metrics } from './metrics';
const inventoryModels = [host, pod, container];
export const inventoryModels = [host, pod, container, awsEC2, awsS3, awsRDS, awsSQS];
export const findInventoryModel = (type: InventoryItemType) => {
const model = inventoryModels.find(m => m.id === type);
@ -24,3 +28,38 @@ export const findInventoryModel = (type: InventoryItemType) => {
}
return model;
};
interface InventoryFields {
message: string[];
host: string;
pod: string;
container: string;
timestamp: string;
tiebreaker: string;
}
const LEGACY_TYPES = ['host', 'pod', 'container'];
const getFieldByType = (type: InventoryItemType, fields: InventoryFields) => {
switch (type) {
case 'pod':
return fields.pod;
case 'host':
return fields.host;
case 'container':
return fields.container;
}
};
export const findInventoryFields = (type: InventoryItemType, fields: InventoryFields) => {
const inventoryModel = findInventoryModel(type);
if (LEGACY_TYPES.includes(type)) {
const id = getFieldByType(type, fields) || inventoryModel.fields.id;
return {
...inventoryModel.fields,
id,
};
} else {
return inventoryModel.fields;
}
};

View file

@ -17,6 +17,10 @@ import { ReactNode, FunctionComponent } from 'react';
import { Layout as HostLayout } from './host/layout';
import { Layout as PodLayout } from './pod/layout';
import { Layout as ContainerLayout } from './container/layout';
import { Layout as AwsEC2Layout } from './aws_ec2/layout';
import { Layout as AwsS3Layout } from './aws_s3/layout';
import { Layout as AwsRDSLayout } from './aws_rds/layout';
import { Layout as AwsSQSLayout } from './aws_sqs/layout';
import { InventoryItemType } from './types';
import { LayoutProps } from '../../public/pages/metrics/types';
@ -28,6 +32,10 @@ const layouts: Layouts = {
host: HostLayout,
pod: PodLayout,
container: ContainerLayout,
awsEC2: AwsEC2Layout,
awsS3: AwsS3Layout,
awsRDS: AwsRDSLayout,
awsSQS: AwsSQSLayout,
};
export const findLayout = (type: InventoryItemType) => {

View file

@ -8,6 +8,10 @@ import { metrics as hostMetrics } from './host/metrics';
import { metrics as sharedMetrics } from './shared/metrics';
import { metrics as podMetrics } from './pod/metrics';
import { metrics as containerMetrics } from './container/metrics';
import { metrics as awsEC2Metrics } from './aws_ec2/metrics';
import { metrics as awsS3Metrics } from './aws_s3/metrics';
import { metrics as awsRDSMetrics } from './aws_rds/metrics';
import { metrics as awsSQSMetrics } from './aws_sqs/metrics';
export const metrics = {
tsvb: {
@ -15,5 +19,9 @@ export const metrics = {
...sharedMetrics.tsvb,
...podMetrics.tsvb,
...containerMetrics.tsvb,
...awsEC2Metrics.tsvb,
...awsS3Metrics.tsvb,
...awsRDSMetrics.tsvb,
...awsSQSMetrics.tsvb,
},
};

View file

@ -4,13 +4,28 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { metrics } from './metrics';
import { InventoryModel } from '../types';
import { nginx as nginxRequiredMetrics } from '../shared/metrics/required_metrics';
export const pod: InventoryModel = {
id: 'pod',
displayName: i18n.translate('xpack.infra.inventoryModel.pod.displayName', {
defaultMessage: 'Kubernetes Pods',
}),
requiredModules: ['kubernetes'],
crosslinkSupport: {
details: true,
logs: true,
apm: true,
uptime: true,
},
fields: {
id: 'kubernetes.pod.uid',
name: 'kubernetes.pod.name',
ip: 'kubernetes.pod.ip',
},
metrics,
requiredMetrics: [
'podOverview',

View file

@ -26,4 +26,5 @@ export const metrics: InventoryMetrics = {
},
snapshot: { cpu, memory, rx, tx },
defaultSnapshot: 'cpu',
defaultTimeRangeInSeconds: 3600, // 1 hour
};

View file

@ -4,54 +4,24 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useMemo } from 'react';
import { EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
import { WaffleMetricControls } from '../../../public/components/waffle/waffle_metric_controls';
import { WaffleGroupByControls } from '../../../public/components/waffle/waffle_group_by_controls';
import { InfraSnapshotMetricType } from '../../../public/graphql/types';
import {
toGroupByOpt,
toMetricOpt,
} from '../../../public/components/inventory/toolbars/toolbar_wrapper';
import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
import { InfraSnapshotMetricType } from '../../graphql/types';
export const PodToolbarItems = (props: ToolbarProps) => {
const options = useMemo(
() =>
[
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.memory,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
].map(toMetricOpt),
[]
);
const groupByOptions = useMemo(
() => ['kubernetes.namespace', 'kubernetes.node.name', 'service.type'].map(toGroupByOpt),
[]
);
const metricTypes = [
InfraSnapshotMetricType.cpu,
InfraSnapshotMetricType.memory,
InfraSnapshotMetricType.rx,
InfraSnapshotMetricType.tx,
];
const groupByFields = ['kubernetes.namespace', 'kubernetes.node.name', 'service.type'];
return (
<>
<EuiFlexItem grow={false}>
<WaffleMetricControls
metric={props.metric}
options={options}
onChange={props.changeMetric}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<WaffleGroupByControls
options={groupByOptions}
groupBy={props.groupBy}
nodeType={props.nodeType}
onChange={props.changeGroupBy}
fields={props.createDerivedIndexPattern('metrics').fields}
onChangeCustomOptions={props.changeCustomOptions}
customOptions={props.customOptions}
/>
</EuiFlexItem>
</>
<MetricsAndGroupByToolbarItems
{...props}
metricTypes={metricTypes}
groupByFields={groupByFields}
/>
);
};

View file

@ -0,0 +1,52 @@
/*
* 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, { useMemo } from 'react';
import { EuiFlexItem } from '@elastic/eui';
import { ToolbarProps } from '../../../../public/components/inventory/toolbars/toolbar';
import { WaffleMetricControls } from '../../../../public/components/waffle/waffle_metric_controls';
import { WaffleGroupByControls } from '../../../../public/components/waffle/waffle_group_by_controls';
import { InfraSnapshotMetricType } from '../../../../public/graphql/types';
import {
toGroupByOpt,
toMetricOpt,
} from '../../../../public/components/inventory/toolbars/toolbar_wrapper';
interface Props extends ToolbarProps {
metricTypes: InfraSnapshotMetricType[];
groupByFields: string[];
}
export const MetricsAndGroupByToolbarItems = (props: Props) => {
const metricOptions = useMemo(() => props.metricTypes.map(toMetricOpt), [props.metricTypes]);
const groupByOptions = useMemo(() => props.groupByFields.map(toGroupByOpt), [
props.groupByFields,
]);
return (
<>
<EuiFlexItem grow={false}>
<WaffleMetricControls
options={metricOptions}
metric={props.metric}
onChange={props.changeMetric}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<WaffleGroupByControls
options={groupByOptions}
groupBy={props.groupBy}
nodeType={props.nodeType}
onChange={props.changeGroupBy}
fields={props.createDerivedIndexPattern('metrics').fields}
onChangeCustomOptions={props.changeCustomOptions}
customOptions={props.customOptions}
/>
</EuiFlexItem>
</>
);
};

View file

@ -35,4 +35,5 @@ export const metrics: InventoryMetrics = {
count,
},
defaultSnapshot: 'count',
defaultTimeRangeInSeconds: 3600,
};

View file

@ -11,6 +11,10 @@ import { HostToolbarItems } from './host/toolbar_items';
import { ContainerToolbarItems } from './container/toolbar_items';
import { PodToolbarItems } from './pod/toolbar_items';
import { ToolbarProps } from '../../public/components/inventory/toolbars/toolbar';
import { AwsEC2ToolbarItems } from './aws_ec2/toolbar_items';
import { AwsS3ToolbarItems } from './aws_s3/toolbar_items';
import { AwsRDSToolbarItems } from './aws_rds/toolbar_items';
import { AwsSQSToolbarItems } from './aws_sqs/toolbar_items';
interface Toolbars {
[type: string]: ReactNode;
@ -20,6 +24,10 @@ const toolbars: Toolbars = {
host: HostToolbarItems,
container: ContainerToolbarItems,
pod: PodToolbarItems,
awsEC2: AwsEC2ToolbarItems,
awsS3: AwsS3ToolbarItems,
awsRDS: AwsRDSToolbarItems,
awsSQS: AwsSQSToolbarItems,
};
export const findToolbar = (type: InventoryItemType) => {

View file

@ -30,6 +30,7 @@ export const InventoryFormatterTypeRT = rt.keyof({
bytes: null,
number: null,
percent: null,
highPercision: null,
});
export type InventoryFormatterType = rt.TypeOf<typeof InventoryFormatterTypeRT>;
export type InventoryItemType = rt.TypeOf<typeof ItemTypeRT>;
@ -72,6 +73,24 @@ export const InventoryMetricRT = rt.keyof({
awsNetworkPackets: null,
awsDiskioBytes: null,
awsDiskioOps: null,
awsEC2CpuUtilization: null,
awsEC2NetworkTraffic: null,
awsEC2DiskIOBytes: null,
awsS3TotalRequests: null,
awsS3NumberOfObjects: null,
awsS3BucketSize: null,
awsS3DownloadBytes: null,
awsS3UploadBytes: null,
awsRDSCpuTotal: null,
awsRDSConnections: null,
awsRDSQueriesExecuted: null,
awsRDSActiveTransactions: null,
awsRDSLatency: null,
awsSQSMessagesVisible: null,
awsSQSMessagesDelayed: null,
awsSQSMessagesSent: null,
awsSQSMessagesEmpty: null,
awsSQSOldestMessage: null,
custom: null,
});
export type InventoryMetric = rt.TypeOf<typeof InventoryMetricRT>;
@ -162,6 +181,8 @@ export const TSVBSeriesRT = rt.intersection([
}),
]);
export type TSVBSeries = rt.TypeOf<typeof TSVBSeriesRT>;
export const TSVBMetricModelRT = rt.intersection([
rt.type({
id: InventoryMetricRT,
@ -176,6 +197,7 @@ export const TSVBMetricModelRT = rt.intersection([
filter: rt.string,
map_field_to: rt.string,
id_type: rt.keyof({ cloud: null, node: null }),
drop_last_bucket: rt.boolean,
}),
]);
@ -267,6 +289,22 @@ export const SnapshotMetricTypeRT = rt.keyof({
tx: null,
rx: null,
logRate: null,
diskIOReadBytes: null,
diskIOWriteBytes: null,
s3TotalRequests: null,
s3NumberOfObjects: null,
s3BucketSize: null,
s3DownloadBytes: null,
s3UploadBytes: null,
rdsConnections: null,
rdsQueriesExecuted: null,
rdsActiveTransactions: null,
rdsLatency: null,
sqsMessagesVisible: null,
sqsMessagesDelayed: null,
sqsMessagesSent: null,
sqsMessagesEmpty: null,
sqsOldestMessage: null,
});
export type SnapshotMetricType = rt.TypeOf<typeof SnapshotMetricTypeRT>;
@ -275,11 +313,25 @@ export interface InventoryMetrics {
tsvb: { [name: string]: TSVBMetricModelCreator };
snapshot: { [name: string]: SnapshotModel };
defaultSnapshot: SnapshotMetricType;
/** This is used by the inventory view to calculate the appropriate amount of time for the metrics detail page. Some metris like awsS3 require multiple days where others like host only need an hour.*/
defaultTimeRangeInSeconds: number;
}
export interface InventoryModel {
id: string;
displayName: string;
requiredModules: string[];
fields: {
id: string;
name: string;
ip?: string;
};
crosslinkSupport: {
details: boolean;
logs: boolean;
apm: boolean;
uptime: boolean;
};
metrics: InventoryMetrics;
requiredMetrics: InventoryMetric[];
}

View file

@ -8,7 +8,6 @@ import React from 'react';
import { InfraWaffleMapOptions, InfraWaffleMapBounds } from '../../lib/lib';
import {
InfraNodeType,
InfraTimerangeInput,
InfraSnapshotMetricInput,
InfraSnapshotGroupbyInput,
} from '../../graphql/types';
@ -23,7 +22,7 @@ export interface LayoutProps {
options: InfraWaffleMapOptions;
nodeType: InfraNodeType;
onDrilldown: (filter: KueryFilterQuery) => void;
timeRange: InfraTimerangeInput;
currentTime: number;
onViewChange: (view: string) => void;
view: string;
boundsOverride: InfraWaffleMapBounds;
@ -42,7 +41,7 @@ export const Layout = (props: LayoutProps) => {
props.groupBy,
props.nodeType,
props.sourceId,
props.timeRange
props.currentTime
);
return (
<>
@ -55,7 +54,7 @@ export const Layout = (props: LayoutProps) => {
loading={loading}
reload={reload}
onDrilldown={props.onDrilldown}
timeRange={props.timeRange}
currentTime={props.currentTime}
onViewChange={props.onViewChange}
view={props.view}
autoBounds={props.autoBounds}

View file

@ -81,6 +81,54 @@ const ToolbarTranslations = {
Count: i18n.translate('xpack.infra.waffle.metricOptions.countText', {
defaultMessage: 'Count',
}),
DiskIOReadBytes: i18n.translate('xpack.infra.waffle.metricOptions.diskIOReadBytes', {
defaultMessage: 'Disk Reads',
}),
DiskIOWriteBytes: i18n.translate('xpack.infra.waffle.metricOptions.diskIOWriteBytes', {
defaultMessage: 'Disk Writes',
}),
s3BucketSize: i18n.translate('xpack.infra.waffle.metricOptions.s3BucketSize', {
defaultMessage: 'Bucket Size',
}),
s3TotalRequests: i18n.translate('xpack.infra.waffle.metricOptions.s3TotalRequests', {
defaultMessage: 'Total Requests',
}),
s3NumberOfObjects: i18n.translate('xpack.infra.waffle.metricOptions.s3NumberOfObjects', {
defaultMessage: 'Number of Objects',
}),
s3DownloadBytes: i18n.translate('xpack.infra.waffle.metricOptions.s3DownloadBytes', {
defaultMessage: 'Downloads (Bytes)',
}),
s3UploadBytes: i18n.translate('xpack.infra.waffle.metricOptions.s3UploadBytes', {
defaultMessage: 'Uploads (Bytes)',
}),
rdsConnections: i18n.translate('xpack.infra.waffle.metricOptions.rdsConnections', {
defaultMessage: 'Connections',
}),
rdsQueriesExecuted: i18n.translate('xpack.infra.waffle.metricOptions.rdsQueriesExecuted', {
defaultMessage: 'Queries Executed',
}),
rdsActiveTransactions: i18n.translate('xpack.infra.waffle.metricOptions.rdsActiveTransactions', {
defaultMessage: 'Active Transactions',
}),
rdsLatency: i18n.translate('xpack.infra.waffle.metricOptions.rdsLatency', {
defaultMessage: 'Latency',
}),
sqsMessagesVisible: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesVisible', {
defaultMessage: 'Messages Available',
}),
sqsMessagesDelayed: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesDelayed', {
defaultMessage: 'Messages Delayed',
}),
sqsMessagesSent: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesSent', {
defaultMessage: 'Messages Added',
}),
sqsMessagesEmpty: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesEmpty', {
defaultMessage: 'Messages Returned Empty',
}),
sqsOldestMessage: i18n.translate('xpack.infra.waffle.metricOptions.sqsOldestMessage', {
defaultMessage: 'Oldest Message',
}),
};
export const toGroupByOpt = (field: string) => ({
@ -126,5 +174,85 @@ export const toMetricOpt = (metric: InfraSnapshotMetricType) => {
text: ToolbarTranslations.Count,
value: InfraSnapshotMetricType.count,
};
case InfraSnapshotMetricType.diskIOReadBytes:
return {
text: ToolbarTranslations.DiskIOReadBytes,
value: InfraSnapshotMetricType.diskIOReadBytes,
};
case InfraSnapshotMetricType.diskIOWriteBytes:
return {
text: ToolbarTranslations.DiskIOWriteBytes,
value: InfraSnapshotMetricType.diskIOWriteBytes,
};
case InfraSnapshotMetricType.s3BucketSize:
return {
text: ToolbarTranslations.s3BucketSize,
value: InfraSnapshotMetricType.s3BucketSize,
};
case InfraSnapshotMetricType.s3TotalRequests:
return {
text: ToolbarTranslations.s3TotalRequests,
value: InfraSnapshotMetricType.s3TotalRequests,
};
case InfraSnapshotMetricType.s3NumberOfObjects:
return {
text: ToolbarTranslations.s3NumberOfObjects,
value: InfraSnapshotMetricType.s3NumberOfObjects,
};
case InfraSnapshotMetricType.s3DownloadBytes:
return {
text: ToolbarTranslations.s3DownloadBytes,
value: InfraSnapshotMetricType.s3DownloadBytes,
};
case InfraSnapshotMetricType.s3UploadBytes:
return {
text: ToolbarTranslations.s3UploadBytes,
value: InfraSnapshotMetricType.s3UploadBytes,
};
case InfraSnapshotMetricType.rdsConnections:
return {
text: ToolbarTranslations.rdsConnections,
value: InfraSnapshotMetricType.rdsConnections,
};
case InfraSnapshotMetricType.rdsQueriesExecuted:
return {
text: ToolbarTranslations.rdsQueriesExecuted,
value: InfraSnapshotMetricType.rdsQueriesExecuted,
};
case InfraSnapshotMetricType.rdsActiveTransactions:
return {
text: ToolbarTranslations.rdsActiveTransactions,
value: InfraSnapshotMetricType.rdsActiveTransactions,
};
case InfraSnapshotMetricType.rdsLatency:
return {
text: ToolbarTranslations.rdsLatency,
value: InfraSnapshotMetricType.rdsLatency,
};
case InfraSnapshotMetricType.sqsMessagesVisible:
return {
text: ToolbarTranslations.sqsMessagesVisible,
value: InfraSnapshotMetricType.sqsMessagesVisible,
};
case InfraSnapshotMetricType.sqsMessagesDelayed:
return {
text: ToolbarTranslations.sqsMessagesDelayed,
value: InfraSnapshotMetricType.sqsMessagesDelayed,
};
case InfraSnapshotMetricType.sqsMessagesSent:
return {
text: ToolbarTranslations.sqsMessagesSent,
value: InfraSnapshotMetricType.sqsMessagesSent,
};
case InfraSnapshotMetricType.sqsMessagesEmpty:
return {
text: ToolbarTranslations.sqsMessagesEmpty,
value: InfraSnapshotMetricType.sqsMessagesEmpty,
};
case InfraSnapshotMetricType.sqsOldestMessage:
return {
text: ToolbarTranslations.sqsOldestMessage,
value: InfraSnapshotMetricType.sqsOldestMessage,
};
}
};

View file

@ -11,12 +11,7 @@ import { get, max, min } from 'lodash';
import React from 'react';
import euiStyled from '../../../../../common/eui_styled_components';
import {
InfraSnapshotMetricType,
InfraSnapshotNode,
InfraNodeType,
InfraTimerangeInput,
} from '../../graphql/types';
import { InfraSnapshotMetricType, InfraSnapshotNode, InfraNodeType } from '../../graphql/types';
import { InfraFormatterType, InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../lib/lib';
import { KueryFilterQuery } from '../../store/local/waffle_filter';
import { createFormatter } from '../../utils/formatters';
@ -34,7 +29,7 @@ interface Props {
loading: boolean;
reload: () => void;
onDrilldown: (filter: KueryFilterQuery) => void;
timeRange: InfraTimerangeInput;
currentTime: number;
onViewChange: (view: string) => void;
view: string;
boundsOverride: InfraWaffleMapBounds;
@ -67,6 +62,38 @@ const METRIC_FORMATTERS: MetricFormatters = {
formatter: InfraFormatterType.abbreviatedNumber,
template: '{{value}}/s',
},
[InfraSnapshotMetricType.diskIOReadBytes]: {
formatter: InfraFormatterType.bytes,
template: '{{value}}/s',
},
[InfraSnapshotMetricType.diskIOWriteBytes]: {
formatter: InfraFormatterType.bytes,
template: '{{value}}/s',
},
[InfraSnapshotMetricType.s3BucketSize]: {
formatter: InfraFormatterType.bytes,
template: '{{value}}',
},
[InfraSnapshotMetricType.s3TotalRequests]: {
formatter: InfraFormatterType.abbreviatedNumber,
template: '{{value}}',
},
[InfraSnapshotMetricType.s3NumberOfObjects]: {
formatter: InfraFormatterType.abbreviatedNumber,
template: '{{value}}',
},
[InfraSnapshotMetricType.s3UploadBytes]: {
formatter: InfraFormatterType.bytes,
template: '{{value}}',
},
[InfraSnapshotMetricType.s3DownloadBytes]: {
formatter: InfraFormatterType.bytes,
template: '{{value}}',
},
[InfraSnapshotMetricType.sqsOldestMessage]: {
formatter: InfraFormatterType.number,
template: '{{value}} seconds',
},
};
const calculateBoundsFromNodes = (nodes: InfraSnapshotNode[]): InfraWaffleMapBounds => {
@ -92,8 +119,8 @@ export const NodesOverview = class extends React.Component<Props, {}> {
nodeType,
reload,
view,
currentTime,
options,
timeRange,
} = this.props;
if (loading) {
return (
@ -152,7 +179,7 @@ export const NodesOverview = class extends React.Component<Props, {}> {
nodes={nodes}
options={options}
formatter={this.formatter}
timeRange={timeRange}
currentTime={currentTime}
onFilter={this.handleDrilldown}
/>
</TableContainer>
@ -163,7 +190,7 @@ export const NodesOverview = class extends React.Component<Props, {}> {
nodes={nodes}
options={options}
formatter={this.formatter}
timeRange={timeRange}
currentTime={currentTime}
onFilter={this.handleDrilldown}
bounds={bounds}
dataBounds={dataBounds}

View file

@ -10,12 +10,7 @@ import { i18n } from '@kbn/i18n';
import { last } from 'lodash';
import React from 'react';
import { createWaffleMapNode } from '../../containers/waffle/nodes_to_wafflemap';
import {
InfraSnapshotNode,
InfraSnapshotNodePath,
InfraTimerangeInput,
InfraNodeType,
} from '../../graphql/types';
import { InfraSnapshotNode, InfraSnapshotNodePath, InfraNodeType } from '../../graphql/types';
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
import { fieldToName } from '../waffle/lib/field_to_display_name';
import { NodeContextMenu } from '../waffle/node_context_menu';
@ -25,7 +20,7 @@ interface Props {
nodeType: InfraNodeType;
options: InfraWaffleMapOptions;
formatter: (subject: string | number) => string;
timeRange: InfraTimerangeInput;
currentTime: number;
onFilter: (filter: string) => void;
}
@ -49,7 +44,7 @@ const getGroupPaths = (path: InfraSnapshotNodePath[]) => {
export const TableView = class extends React.PureComponent<Props, State> {
public readonly state: State = initialState;
public render() {
const { nodes, options, formatter, timeRange, nodeType } = this.props;
const { nodes, options, formatter, currentTime, nodeType } = this.props;
const columns = [
{
field: 'name',
@ -68,7 +63,7 @@ export const TableView = class extends React.PureComponent<Props, State> {
node={item.node}
nodeType={nodeType}
closePopover={this.closePopoverFor(uniqueID)}
timeRange={timeRange}
currentTime={currentTime}
isPopoverOpen={this.state.isPopoverOpen.includes(uniqueID)}
options={options}
popoverPosition="rightCenter"

View file

@ -7,7 +7,7 @@
import React from 'react';
import euiStyled from '../../../../../common/eui_styled_components';
import { InfraNodeType, InfraTimerangeInput } from '../../graphql/types';
import { InfraNodeType } from '../../graphql/types';
import {
InfraWaffleMapBounds,
InfraWaffleMapGroupOfGroups,
@ -23,7 +23,7 @@ interface Props {
formatter: (val: number) => string;
bounds: InfraWaffleMapBounds;
nodeType: InfraNodeType;
timeRange: InfraTimerangeInput;
currentTime: number;
}
export const GroupOfGroups: React.FC<Props> = props => {
@ -41,7 +41,7 @@ export const GroupOfGroups: React.FC<Props> = props => {
formatter={props.formatter}
bounds={props.bounds}
nodeType={props.nodeType}
timeRange={props.timeRange}
currentTime={props.currentTime}
/>
))}
</Groups>

View file

@ -7,7 +7,7 @@
import React from 'react';
import euiStyled from '../../../../../common/eui_styled_components';
import { InfraNodeType, InfraTimerangeInput } from '../../graphql/types';
import { InfraNodeType } from '../../graphql/types';
import {
InfraWaffleMapBounds,
InfraWaffleMapGroupOfNodes,
@ -24,7 +24,7 @@ interface Props {
isChild: boolean;
bounds: InfraWaffleMapBounds;
nodeType: InfraNodeType;
timeRange: InfraTimerangeInput;
currentTime: number;
}
export const GroupOfNodes: React.FC<Props> = ({
@ -35,7 +35,7 @@ export const GroupOfNodes: React.FC<Props> = ({
isChild = false,
bounds,
nodeType,
timeRange,
currentTime,
}) => {
const width = group.width > 200 ? group.width : 200;
return (
@ -51,7 +51,7 @@ export const GroupOfNodes: React.FC<Props> = ({
formatter={formatter}
bounds={bounds}
nodeType={nodeType}
timeRange={timeRange}
currentTime={currentTime}
/>
))}
</Nodes>

View file

@ -10,6 +10,14 @@ interface Lookup {
[id: string]: string;
}
const availabilityZoneName = i18n.translate('xpack.infra.groupByDisplayNames.availabilityZone', {
defaultMessage: 'Availability zone',
});
const machineTypeName = i18n.translate('xpack.infra.groupByDisplayNames.machineType', {
defaultMessage: 'Machine type',
});
export const fieldToName = (field: string) => {
const LOOKUP: Lookup = {
'kubernetes.namespace': i18n.translate('xpack.infra.groupByDisplayNames.kubernetesNamespace', {
@ -21,12 +29,8 @@ export const fieldToName = (field: string) => {
'host.name': i18n.translate('xpack.infra.groupByDisplayNames.hostName', {
defaultMessage: 'Host',
}),
'cloud.availability_zone': i18n.translate('xpack.infra.groupByDisplayNames.availabilityZone', {
defaultMessage: 'Availability zone',
}),
'cloud.machine.type': i18n.translate('xpack.infra.groupByDisplayNames.machineType', {
defaultMessage: 'Machine type',
}),
'cloud.availability_zone': availabilityZoneName,
'cloud.machine.type': machineTypeName,
'cloud.project.id': i18n.translate('xpack.infra.groupByDisplayNames.projectID', {
defaultMessage: 'Project ID',
}),
@ -36,6 +40,32 @@ export const fieldToName = (field: string) => {
'service.type': i18n.translate('xpack.infra.groupByDisplayNames.serviceType', {
defaultMessage: 'Service type',
}),
'aws.cloud.availability_zone': availabilityZoneName,
'aws.cloud.machine.type': machineTypeName,
'aws.tags': i18n.translate('xpack.infra.groupByDisplayNames.tags', {
defaultMessage: 'Tags',
}),
'aws.ec2.instance.image.id': i18n.translate('xpack.infra.groupByDisplayNames.image', {
defaultMessage: 'Image',
}),
'aws.ec2.instance.state.name': i18n.translate('xpack.infra.groupByDisplayNames.state.name', {
defaultMessage: 'State',
}),
'cloud.region': i18n.translate('xpack.infra.groupByDisplayNames.cloud.region', {
defaultMessage: 'Region',
}),
'aws.rds.db_instance.class': i18n.translate(
'xpack.infra.groupByDisplayNames.rds.db_instance.class',
{
defaultMessage: 'Instance Class',
}
),
'aws.rds.db_instance.status': i18n.translate(
'xpack.infra.groupByDisplayNames.rds.db_instance.status',
{
defaultMessage: 'Status',
}
),
};
return LOOKUP[field] || field;
};

View file

@ -11,7 +11,7 @@ import {
isWaffleMapGroupWithGroups,
isWaffleMapGroupWithNodes,
} from '../../containers/waffle/type_guards';
import { InfraSnapshotNode, InfraNodeType, InfraTimerangeInput } from '../../graphql/types';
import { InfraSnapshotNode, InfraNodeType } from '../../graphql/types';
import { InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../lib/lib';
import { AutoSizer } from '../auto_sizer';
import { GroupOfGroups } from './group_of_groups';
@ -24,7 +24,7 @@ interface Props {
nodeType: InfraNodeType;
options: InfraWaffleMapOptions;
formatter: (subject: string | number) => string;
timeRange: InfraTimerangeInput;
currentTime: number;
onFilter: (filter: string) => void;
bounds: InfraWaffleMapBounds;
dataBounds: InfraWaffleMapBounds;
@ -33,7 +33,7 @@ interface Props {
export const Map: React.FC<Props> = ({
nodes,
options,
timeRange,
currentTime,
onFilter,
formatter,
bounds,
@ -59,7 +59,7 @@ export const Map: React.FC<Props> = ({
formatter={formatter}
bounds={bounds}
nodeType={nodeType}
timeRange={timeRange}
currentTime={currentTime}
/>
);
}
@ -74,7 +74,7 @@ export const Map: React.FC<Props> = ({
isChild={false}
bounds={bounds}
nodeType={nodeType}
timeRange={timeRange}
currentTime={currentTime}
/>
);
}

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import moment from 'moment';
import { darken, readableColor } from 'polished';
import React from 'react';
@ -12,7 +11,7 @@ import { i18n } from '@kbn/i18n';
import { ConditionalToolTip } from './conditional_tooltip';
import euiStyled from '../../../../../common/eui_styled_components';
import { InfraTimerangeInput, InfraNodeType } from '../../graphql/types';
import { InfraNodeType } from '../../graphql/types';
import { InfraWaffleMapBounds, InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
import { colorFromValue } from './lib/color_from_value';
import { NodeContextMenu } from './node_context_menu';
@ -30,13 +29,13 @@ interface Props {
formatter: (val: number) => string;
bounds: InfraWaffleMapBounds;
nodeType: InfraNodeType;
timeRange: InfraTimerangeInput;
currentTime: number;
}
export const Node = class extends React.PureComponent<Props, State> {
public readonly state: State = initialState;
public render() {
const { nodeType, node, options, squareSize, bounds, formatter, timeRange } = this.props;
const { nodeType, node, options, squareSize, bounds, formatter, currentTime } = this.props;
const { isPopoverOpen } = this.state;
const { metric } = node;
const valueMode = squareSize > 70;
@ -44,12 +43,6 @@ export const Node = class extends React.PureComponent<Props, State> {
const rawValue = (metric && metric.value) || 0;
const color = colorFromValue(options.legend, rawValue, bounds);
const value = formatter(rawValue);
const newTimerange = {
...timeRange,
from: moment(timeRange.to)
.subtract(1, 'hour')
.valueOf(),
};
const nodeAriaLabel = i18n.translate('xpack.infra.node.ariaLabel', {
defaultMessage: '{nodeName}, click to open menu',
values: { nodeName: node.name },
@ -61,7 +54,7 @@ export const Node = class extends React.PureComponent<Props, State> {
isPopoverOpen={isPopoverOpen}
closePopover={this.closePopover}
options={options}
timeRange={newTimerange}
currentTime={currentTime}
popoverPosition="downCenter"
>
<ConditionalToolTip

View file

@ -15,14 +15,15 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { UICapabilities } from 'ui/capabilities';
import { injectUICapabilities } from 'ui/capabilities/react';
import { InfraNodeType, InfraTimerangeInput } from '../../graphql/types';
import { InfraNodeType } from '../../graphql/types';
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
import { getNodeDetailUrl, getNodeLogsUrl } from '../../pages/link_to';
import { createUptimeLink } from './lib/create_uptime_link';
import { findInventoryModel } from '../../../common/inventory_models';
interface Props {
options: InfraWaffleMapOptions;
timeRange: InfraTimerangeInput;
currentTime: number;
children: any;
node: InfraWaffleMapNode;
nodeType: InfraNodeType;
@ -35,25 +36,21 @@ interface Props {
export const NodeContextMenu = injectUICapabilities(
({
options,
timeRange,
currentTime,
children,
node,
isPopoverOpen,
closePopover,
nodeType,
uiCapabilities,
popoverPosition,
}: Props) => {
const inventoryModel = findInventoryModel(nodeType);
// Due to the changing nature of the fields between APM and this UI,
// We need to have some exceptions until 7.0 & ECS is finalized. Reference
// #26620 for the details for these fields.
// TODO: This is tech debt, remove it after 7.0 & ECS migration.
const APM_FIELDS = {
[InfraNodeType.host]: 'host.hostname',
[InfraNodeType.container]: 'container.id',
[InfraNodeType.pod]: 'kubernetes.pod.uid',
};
const apmField = nodeType === InfraNodeType.host ? 'host.hostname' : inventoryModel.fields.id;
const nodeLogsMenuItem = {
name: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', {
@ -62,11 +59,12 @@ export const NodeContextMenu = injectUICapabilities(
href: getNodeLogsUrl({
nodeType,
nodeId: node.id,
time: timeRange.to,
time: currentTime,
}),
'data-test-subj': 'viewLogsContextMenuItem',
};
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
const nodeDetailMenuItem = {
name: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', {
defaultMessage: 'View metrics',
@ -74,45 +72,47 @@ export const NodeContextMenu = injectUICapabilities(
href: getNodeDetailUrl({
nodeType,
nodeId: node.id,
from: timeRange.from,
to: timeRange.to,
from: nodeDetailFrom,
to: currentTime,
}),
};
const apmTracesMenuItem = {
name: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', {
defaultMessage: 'View {nodeType} APM traces',
values: { nodeType },
defaultMessage: 'View APM traces',
}),
href: `../app/apm#/traces?_g=()&kuery=${APM_FIELDS[nodeType]}:"${node.id}"`,
href: `../app/apm#/traces?_g=()&kuery=${apmField}:"${node.id}"`,
'data-test-subj': 'viewApmTracesContextMenuItem',
};
const uptimeMenuItem = {
name: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', {
defaultMessage: 'View {nodeType} in Uptime',
values: { nodeType },
defaultMessage: 'View in Uptime',
}),
href: createUptimeLink(options, nodeType, node),
};
const showLogsLink = node.id && uiCapabilities.logs.show;
const showAPMTraceLink = uiCapabilities.apm && uiCapabilities.apm.show;
const showDetail = inventoryModel.crosslinkSupport.details;
const showLogsLink =
inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities.logs.show;
const showAPMTraceLink =
inventoryModel.crosslinkSupport.apm && uiCapabilities.apm && uiCapabilities.apm.show;
const showUptimeLink =
[InfraNodeType.pod, InfraNodeType.container].includes(nodeType) || node.ip;
inventoryModel.crosslinkSupport.uptime &&
([InfraNodeType.pod, InfraNodeType.container].includes(nodeType) || node.ip);
const panels: EuiContextMenuPanelDescriptor[] = [
{
id: 0,
title: '',
items: [
...(showLogsLink ? [nodeLogsMenuItem] : []),
nodeDetailMenuItem,
...(showAPMTraceLink ? [apmTracesMenuItem] : []),
...(showUptimeLink ? [uptimeMenuItem] : []),
],
},
const items = [
...(showLogsLink ? [nodeLogsMenuItem] : []),
...(showDetail ? [nodeDetailMenuItem] : []),
...(showAPMTraceLink ? [apmTracesMenuItem] : []),
...(showUptimeLink ? [uptimeMenuItem] : []),
];
const panels: EuiContextMenuPanelDescriptor[] = [{ id: 0, title: '', items }];
// If there is nothing to show then we need to return the child as is
if (items.length === 0) {
return <>{children}</>;
}
return (
<EuiPopover

View file

@ -4,8 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiPopover, EuiContextMenu, EuiFilterButton, EuiFilterGroup } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import {
EuiPopover,
EuiContextMenu,
EuiFilterButton,
EuiFilterGroup,
EuiContextMenuPanelDescriptor,
} from '@elastic/eui';
import React, { useCallback, useState, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
@ -16,6 +21,7 @@ import {
InfraSnapshotGroupbyInput,
} from '../../graphql/types';
import { findInventoryModel } from '../../../common/inventory_models';
import { InventoryItemType } from '../../../common/inventory_models/types';
interface WaffleInventorySwitcherProps {
nodeType: InfraNodeType;
@ -24,6 +30,11 @@ interface WaffleInventorySwitcherProps {
changeMetric: (metric: InfraSnapshotMetricInput) => void;
}
const getDisplayNameForType = (type: InventoryItemType) => {
const inventoryModel = findInventoryModel(type);
return inventoryModel.displayName;
};
export const WaffleInventorySwitcher: React.FC<WaffleInventorySwitcherProps> = ({
changeNodeType,
changeGroupBy,
@ -48,44 +59,62 @@ export const WaffleInventorySwitcher: React.FC<WaffleInventorySwitcherProps> = (
const goToHost = useCallback(() => goToNodeType('host' as InfraNodeType), [goToNodeType]);
const goToK8 = useCallback(() => goToNodeType('pod' as InfraNodeType), [goToNodeType]);
const goToDocker = useCallback(() => goToNodeType('container' as InfraNodeType), [goToNodeType]);
const goToAwsEC2 = useCallback(() => goToNodeType('awsEC2' as InfraNodeType), [goToNodeType]);
const goToAwsS3 = useCallback(() => goToNodeType('awsS3' as InfraNodeType), [goToNodeType]);
const goToAwsRDS = useCallback(() => goToNodeType('awsRDS' as InfraNodeType), [goToNodeType]);
const goToAwsSQS = useCallback(() => goToNodeType('awsSQS' as InfraNodeType), [goToNodeType]);
const panels = useMemo(
() => [
{
id: 0,
items: [
{
name: i18n.translate('xpack.infra.waffle.nodeTypeSwitcher.hostsLabel', {
defaultMessage: 'Hosts',
}),
icon: 'host',
onClick: goToHost,
},
{
name: 'Kubernetes',
icon: 'kubernetes',
onClick: goToK8,
},
{
name: 'Docker',
icon: 'docker',
onClick: goToDocker,
},
],
},
],
[goToDocker, goToHost, goToK8]
() =>
[
{
id: 'firstPanel',
items: [
{
name: getDisplayNameForType('host'),
onClick: goToHost,
},
{
name: getDisplayNameForType('pod'),
onClick: goToK8,
},
{
name: getDisplayNameForType('container'),
onClick: goToDocker,
},
{
name: 'AWS',
panel: 'awsPanel',
},
],
},
{
id: 'awsPanel',
title: 'AWS',
items: [
{
name: getDisplayNameForType('awsEC2'),
onClick: goToAwsEC2,
},
{
name: getDisplayNameForType('awsS3'),
onClick: goToAwsS3,
},
{
name: getDisplayNameForType('awsRDS'),
onClick: goToAwsRDS,
},
{
name: getDisplayNameForType('awsSQS'),
onClick: goToAwsSQS,
},
],
},
] as EuiContextMenuPanelDescriptor[],
[goToAwsEC2, goToAwsRDS, goToAwsS3, goToAwsSQS, goToDocker, goToHost, goToK8]
);
const selectedText = useMemo(() => {
switch (nodeType) {
case InfraNodeType.host:
return i18n.translate('xpack.infra.waffle.nodeTypeSwitcher.hostsLabel', {
defaultMessage: 'Hosts',
});
case InfraNodeType.pod:
return 'Kubernetes';
case InfraNodeType.container:
return 'Docker';
}
return getDisplayNameForType(nodeType);
}, [nodeType]);
return (
@ -107,7 +136,7 @@ export const WaffleInventorySwitcher: React.FC<WaffleInventorySwitcherProps> = (
withTitle
anchorPosition="downLeft"
>
<EuiContextMenu initialPanelId={0} panels={panels} />
<EuiContextMenu initialPanelId="firstPanel" panels={panels} />
</EuiPopover>
</EuiFilterGroup>
);

View file

@ -44,7 +44,7 @@ export const WaffleMetricControls = class extends React.PureComponent<Props, Sta
}
const currentLabel = options.find(o => o.value === metric.type);
if (!currentLabel) {
return 'null';
return null;
}
const panels: EuiContextMenuPanelDescriptor[] = [
{

View file

@ -12,7 +12,6 @@ import {
InfraNodeType,
InfraSnapshotMetricInput,
InfraSnapshotGroupbyInput,
InfraTimerangeInput,
} from '../../graphql/types';
import { throwErrors, createPlainError } from '../../../common/runtime_types';
import { useHTTPRequest } from '../../hooks/use_http_request';
@ -27,7 +26,7 @@ export function useSnapshot(
groupBy: InfraSnapshotGroupbyInput[],
nodeType: InfraNodeType,
sourceId: string,
timerange: InfraTimerangeInput
currentTime: number
) {
const decodeResponse = (response: any) => {
return pipe(
@ -36,6 +35,12 @@ export function useSnapshot(
);
};
const timerange = {
interval: '1m',
to: currentTime,
from: currentTime - 360 * 1000,
};
const { error, loading, response, makeRequest } = useHTTPRequest<SnapshotNodeResponse>(
'/api/metrics/snapshot',
'POST',

View file

@ -2129,7 +2129,8 @@
"isDeprecated": false,
"deprecationReason": null
},
{ "name": "host", "description": "", "isDeprecated": false, "deprecationReason": null }
{ "name": "host", "description": "", "isDeprecated": false, "deprecationReason": null },
{ "name": "awsEC2", "description": "", "isDeprecated": false, "deprecationReason": null }
],
"possibleTypes": null
},
@ -2191,7 +2192,9 @@
{ "name": "memory", "description": "", "isDeprecated": false, "deprecationReason": null },
{ "name": "tx", "description": "", "isDeprecated": false, "deprecationReason": null },
{ "name": "rx", "description": "", "isDeprecated": false, "deprecationReason": null },
{ "name": "logRate", "description": "", "isDeprecated": false, "deprecationReason": null }
{ "name": "logRate", "description": "", "isDeprecated": false, "deprecationReason": null },
{ "name": "diskIOReadBytes", "description": "", "isDeprecated": false, "deprecationReason": null },
{ "name": "diskIOWriteBytes", "description": "", "isDeprecated": false, "deprecationReason": null }
],
"possibleTypes": null
},
@ -2585,6 +2588,24 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "awsEC2CpuUtilization",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "awsEC2NetworkTraffic",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "awsEC2DiskIOBytes",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{ "name": "custom", "description": "", "isDeprecated": false, "deprecationReason": null }
],
"possibleTypes": null

View file

@ -554,6 +554,10 @@ export enum InfraNodeType {
pod = 'pod',
container = 'container',
host = 'host',
awsEC2 = 'awsEC2',
awsS3 = 'awsS3',
awsRDS = 'awsRDS',
awsSQS = 'awsSQS',
}
export enum InfraSnapshotMetricType {
@ -564,6 +568,22 @@ export enum InfraSnapshotMetricType {
tx = 'tx',
rx = 'rx',
logRate = 'logRate',
diskIOReadBytes = 'diskIOReadBytes',
diskIOWriteBytes = 'diskIOWriteBytes',
s3TotalRequests = 's3TotalRequests',
s3NumberOfObjects = 's3NumberOfObjects',
s3BucketSize = 's3BucketSize',
s3DownloadBytes = 's3DownloadBytes',
s3UploadBytes = 's3UploadBytes',
rdsConnections = 'rdsConnections',
rdsQueriesExecuted = 'rdsQueriesExecuted',
rdsActiveTransactions = 'rdsActiveTransactions',
rdsLatency = 'rdsLatency',
sqsMessagesVisible = 'sqsMessagesVisible',
sqsMessagesDelayed = 'sqsMessagesDelayed',
sqsMessagesSent = 'sqsMessagesSent',
sqsMessagesEmpty = 'sqsMessagesEmpty',
sqsOldestMessage = 'sqsOldestMessage',
}
export enum InfraMetric {
@ -604,6 +624,24 @@ export enum InfraMetric {
awsNetworkPackets = 'awsNetworkPackets',
awsDiskioBytes = 'awsDiskioBytes',
awsDiskioOps = 'awsDiskioOps',
awsEC2CpuUtilization = 'awsEC2CpuUtilization',
awsEC2DiskIOBytes = 'awsEC2DiskIOBytes',
awsEC2NetworkTraffic = 'awsEC2NetworkTraffic',
awsS3TotalRequests = 'awsS3TotalRequests',
awsS3NumberOfObjects = 'awsS3NumberOfObjects',
awsS3BucketSize = 'awsS3BucketSize',
awsS3DownloadBytes = 'awsS3DownloadBytes',
awsS3UploadBytes = 'awsS3UploadBytes',
awsRDSCpuTotal = 'awsRDSCpuTotal',
awsRDSConnections = 'awsRDSConnections',
awsRDSQueriesExecuted = 'awsRDSQueriesExecuted',
awsRDSActiveTransactions = 'awsRDSActiveTransactions',
awsRDSLatency = 'awsRDSLatency',
awsSQSMessagesVisible = 'awsSQSMessagesVisible',
awsSQSMessagesDelayed = 'awsSQSMessagesDelayed',
awsSQSMessagesSent = 'awsSQSMessagesSent',
awsSQSMessagesEmpty = 'awsSQSMessagesEmpty',
awsSQSOldestMessage = 'awsSQSOldestMessage',
custom = 'custom',
}

View file

@ -21,7 +21,7 @@ export const SnapshotPageContent: React.FC = () => (
<WithWaffleFilter indexPattern={createDerivedIndexPattern('metrics')}>
{({ filterQueryAsJson, applyFilterQuery }) => (
<WithWaffleTime>
{({ currentTimeRange, isAutoReloading }) => (
{({ currentTime }) => (
<WithWaffleOptions>
{({
metric,
@ -33,12 +33,12 @@ export const SnapshotPageContent: React.FC = () => (
boundsOverride,
}) => (
<Layout
currentTime={currentTime}
filterQuery={filterQueryAsJson}
metric={metric}
groupBy={groupBy}
nodeType={nodeType}
sourceId={sourceId}
timeRange={currentTimeRange}
options={{
...wafflemap,
metric,

View file

@ -11,19 +11,22 @@ import { RedirectToLogs } from './redirect_to_logs';
import { RedirectToNodeDetail } from './redirect_to_node_detail';
import { RedirectToNodeLogs } from './redirect_to_node_logs';
import { RedirectToHostDetailViaIP } from './redirect_to_host_detail_via_ip';
import { inventoryModels } from '../../../common/inventory_models';
interface LinkToPageProps {
match: RouteMatch<{}>;
}
const ITEM_TYPES = inventoryModels.map(m => m.id).join('|');
export const LinkToPage: React.FC<LinkToPageProps> = props => (
<Switch>
<Route
path={`${props.match.url}/:sourceId?/:nodeType(host|container|pod)-logs/:nodeId`}
path={`${props.match.url}/:sourceId?/:nodeType(${ITEM_TYPES})-logs/:nodeId`}
component={RedirectToNodeLogs}
/>
<Route
path={`${props.match.url}/:nodeType(host|container|pod)-detail/:nodeId`}
path={`${props.match.url}/:nodeType(${ITEM_TYPES})-detail/:nodeId`}
component={RedirectToNodeDetail}
/>
<Route

View file

@ -14,9 +14,10 @@ import { LoadingPage } from '../../components/loading_page';
import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter';
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
import { replaceSourceIdInQueryString } from '../../containers/source_id';
import { InfraNodeType } from '../../graphql/types';
import { InfraNodeType, SourceConfigurationFields } from '../../graphql/types';
import { getFilterFromLocation, getTimeFromLocation } from './query_params';
import { useSource } from '../../containers/source/source';
import { findInventoryFields } from '../../../common/inventory_models';
type RedirectToNodeLogsType = RouteComponentProps<{
nodeId: string;
@ -24,6 +25,11 @@ type RedirectToNodeLogsType = RouteComponentProps<{
sourceId?: string;
}>;
const getFieldByNodeType = (nodeType: InfraNodeType, fields: SourceConfigurationFields.Fields) => {
const inventoryFields = findInventoryFields(nodeType, fields);
return inventoryFields.id;
};
export const RedirectToNodeLogs = ({
match: {
params: { nodeId, nodeType, sourceId = 'default' },
@ -50,7 +56,7 @@ export const RedirectToNodeLogs = ({
return null;
}
const nodeFilter = `${configuration.fields[nodeType]}: ${nodeId}`;
const nodeFilter = `${getFieldByNodeType(nodeType, configuration.fields)}: ${nodeId}`;
const userFilter = getFilterFromLocation(location);
const filter = userFilter ? `(${nodeFilter}) and (${userFilter})` : nodeFilter;

View file

@ -0,0 +1,11 @@
/*
* 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.
*/
export const formatHighPercision = (val: number) => {
return Number(val).toLocaleString('en', {
maximumFractionDigits: 5,
});
};

View file

@ -10,6 +10,7 @@ import { createBytesFormatter } from './bytes';
import { formatNumber } from './number';
import { formatPercent } from './percent';
import { InventoryFormatterType } from '../../../common/inventory_models/types';
import { formatHighPercision } from './high_precision';
export const FORMATTERS = {
number: formatNumber,
@ -21,6 +22,7 @@ export const FORMATTERS = {
// bytes in bits formatted string out
bits: createBytesFormatter(InfraWaffleMapDataFormat.bitsDecimal),
percent: formatPercent,
highPercision: formatHighPercision,
};
export const createFormatter = (format: InventoryFormatterType, template: string = '{{value}}') => (

View file

@ -580,6 +580,10 @@ export enum InfraNodeType {
pod = 'pod',
container = 'container',
host = 'host',
awsEC2 = 'awsEC2',
awsS3 = 'awsS3',
awsRDS = 'awsRDS',
awsSQS = 'awsSQS'
}
export enum InfraSnapshotMetricType {
@ -590,6 +594,22 @@ export enum InfraSnapshotMetricType {
tx = 'tx',
rx = 'rx',
logRate = 'logRate',
diskIOReadBytes = 'diskIOReadBytes',
diskIOWriteBytes = 'diskIOWriteBytes',
s3TotalRequests = 's3TotalRequests',
s3NumberOfObjects = 's3NumberOfObjects',
s3BucketSize = 's3BucketSize',
s3DownloadBytes = 's3DownloadBytes',
s3UploadBytes = 's3UploadBytes',
rdsConnections = 'rdsConnections',
rdsQueriesExecuted = 'rdsQueriesExecuted',
rdsActiveTransactions = 'rdsActiveTransactions',
rdsLatency = 'rdsLatency',
sqsMessagesVisible = 'sqsOldestMessage',
sqsMessagesDelayed = 'sqsMessagesDelayed',
sqsMessagesSent = 'sqsMessagesSent',
sqsMessagesEmpty = 'sqsMessagesEmpty',
sqsOldestMessage = 'sqsOldestMessage',
}
export enum InfraMetric {
@ -630,6 +650,24 @@ export enum InfraMetric {
awsNetworkPackets = 'awsNetworkPackets',
awsDiskioBytes = 'awsDiskioBytes',
awsDiskioOps = 'awsDiskioOps',
awsEC2CpuUtilization = 'awsEC2CpuUtilization',
awsEC2DiskIOBytes = 'awsEC2DiskIOBytes',
awsEC2NetworkTraffic = 'awsEC2NetworkTraffic',
awsS3TotalRequests = 'awsS3TotalRequests',
awsS3NumberOfObjects = 'awsS3NumberOfObjects',
awsS3BucketSize = 'awsS3BucketSize',
awsS3DownloadBytes = 'awsS3DownloadBytes',
awsS3UploadBytes = 'awsS3UploadBytes',
awsRDSCpuTotal = 'awsRDSCpuTotal',
awsRDSConnections = 'awsRDSConnections',
awsRDSQueriesExecuted = 'awsRDSQueriesExecuted',
awsRDSActiveTransactions = 'awsRDSActiveTransactions',
awsRDSLatency = 'awsRDSLatency',
awsSQSMessagesVisible = 'awsSQSMessagesVisible',
awsSQSMessagesDelayed = 'awsSQSMessagesDelayed',
awsSQSMessagesSent = 'awsSQSMessagesSent',
awsSQSMessagesEmpty = 'awsSQSMessagesEmpty',
awsSQSOldestMessage = 'awsSQSOldestMessage',
custom = 'custom',
}

View file

@ -7,11 +7,11 @@
import { i18n } from '@kbn/i18n';
import { flatten, get } from 'lodash';
import { KibanaRequest, RequestHandlerContext } from 'src/core/server';
import { InfraMetric, InfraMetricData, InfraNodeType } from '../../../graphql/types';
import { InfraMetric, InfraMetricData } from '../../../graphql/types';
import { KibanaFramework } from '../framework/kibana_framework_adapter';
import { InfraMetricsAdapter, InfraMetricsRequestOptions } from './adapter_types';
import { checkValidNode } from './lib/check_valid_node';
import { metrics } from '../../../../common/inventory_models';
import { metrics, findInventoryFields } from '../../../../common/inventory_models';
import { TSVBMetricModelCreator } from '../../../../common/inventory_models/types';
import { calculateMetricInterval } from '../../../utils/calculate_metric_interval';
@ -27,13 +27,10 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter {
options: InfraMetricsRequestOptions,
rawRequest: KibanaRequest // NP_TODO: Temporarily needed until metrics getVisData no longer needs full request
): Promise<InfraMetricData[]> {
const fields = {
[InfraNodeType.host]: options.sourceConfiguration.fields.host,
[InfraNodeType.container]: options.sourceConfiguration.fields.container,
[InfraNodeType.pod]: options.sourceConfiguration.fields.pod,
};
const indexPattern = `${options.sourceConfiguration.metricAlias},${options.sourceConfiguration.logAlias}`;
const nodeField = fields[options.nodeType];
const fields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields);
const nodeField = fields.id;
const search = <Aggregation>(searchOptions: object) =>
this.framework.callWithRequest<{}, Aggregation>(requestContext, 'search', searchOptions);

View file

@ -4,21 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { InfraNodeType } from '../graphql/types';
// Used for metadata and snapshots resolvers to find the field that contains
// a displayable name of a node.
// Intentionally not the same as xpack.infra.sources.default.fields.{host,container,pod}.
// TODO: consider moving this to source configuration too.
export const NAME_FIELDS = {
[InfraNodeType.host]: 'host.name',
[InfraNodeType.pod]: 'kubernetes.pod.name',
[InfraNodeType.container]: 'container.name',
};
export const IP_FIELDS = {
[InfraNodeType.host]: 'host.ip',
[InfraNodeType.pod]: 'kubernetes.pod.ip',
[InfraNodeType.container]: 'container.ip_address',
};
export const CLOUD_METRICS_MODULES = ['aws'];

View file

@ -0,0 +1,59 @@
/*
* 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 { uniq } from 'lodash';
import { RequestHandlerContext } from 'kibana/server';
import { InfraSnapshotRequestOptions } from './types';
import { InfraTimerangeInput } from '../../../public/graphql/types';
import { getMetricsAggregations } from './query_helpers';
import { calculateMetricInterval } from '../../utils/calculate_metric_interval';
import { SnapshotModel, SnapshotModelMetricAggRT } from '../../../common/inventory_models/types';
import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter';
export const createTimeRangeWithInterval = async (
framework: KibanaFramework,
requestContext: RequestHandlerContext,
options: InfraSnapshotRequestOptions
): Promise<InfraTimerangeInput> => {
const aggregations = getMetricsAggregations(options);
const modules = aggregationsToModules(aggregations);
const interval =
(await calculateMetricInterval(
framework,
requestContext,
{
indexPattern: options.sourceConfiguration.metricAlias,
timestampField: options.sourceConfiguration.fields.timestamp,
timerange: { from: options.timerange.from, to: options.timerange.to },
},
modules,
options.nodeType
)) || 60000;
return {
interval: `${interval}s`,
from: options.timerange.to - interval * 5000, // We need at least 5 buckets worth of data
to: options.timerange.to,
};
};
const aggregationsToModules = (aggregations: SnapshotModel): string[] => {
return uniq(
Object.values(aggregations)
.reduce((modules, agg) => {
if (SnapshotModelMetricAggRT.is(agg)) {
return modules.concat(Object.values(agg).map(a => a?.field));
}
return modules;
}, [] as Array<string | undefined>)
.filter(v => v)
.map(field =>
field!
.split(/\./)
.slice(0, 2)
.join('.')
)
) as string[];
};

View file

@ -4,11 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { get } from 'lodash';
import { i18n } from '@kbn/i18n';
import { findInventoryModel } from '../../../common/inventory_models/index';
import { InfraSnapshotRequestOptions } from './snapshot';
import { NAME_FIELDS } from '../constants';
import { findInventoryModel, findInventoryFields } from '../../../common/inventory_models/index';
import { InfraSnapshotRequestOptions } from './types';
import { getIntervalInSeconds } from '../../utils/get_interval_in_seconds';
import { SnapshotModelRT, SnapshotModel } from '../../../common/inventory_models/types';
@ -21,28 +19,35 @@ interface GroupBySource {
};
}
export const getFieldByNodeType = (options: InfraSnapshotRequestOptions) => {
const inventoryFields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields);
return inventoryFields.id;
};
export const getGroupedNodesSources = (options: InfraSnapshotRequestOptions) => {
const fields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields);
const sources: GroupBySource[] = options.groupBy.map(gb => {
return { [`${gb.field}`]: { terms: { field: gb.field } } };
});
sources.push({
id: {
terms: { field: options.sourceConfiguration.fields[options.nodeType] },
terms: { field: fields.id },
},
});
sources.push({
name: { terms: { field: NAME_FIELDS[options.nodeType], missing_bucket: true } },
name: { terms: { field: fields.name, missing_bucket: true } },
});
return sources;
};
export const getMetricsSources = (options: InfraSnapshotRequestOptions) => {
return [{ id: { terms: { field: options.sourceConfiguration.fields[options.nodeType] } } }];
const fields = findInventoryFields(options.nodeType, options.sourceConfiguration.fields);
return [{ id: { terms: { field: fields.id } } }];
};
export const getMetricsAggregations = (options: InfraSnapshotRequestOptions): SnapshotModel => {
const model = findInventoryModel(options.nodeType);
const aggregation = get(model, ['metrics', 'snapshot', options.metric.type]);
const inventoryModel = findInventoryModel(options.nodeType);
const aggregation = inventoryModel.metrics.snapshot?.[options.metric.type];
if (!SnapshotModelRT.is(aggregation)) {
throw new Error(
i18n.translate('xpack.infra.snapshot.missingSnapshotMetricError', {

Some files were not shown because too many files have changed in this diff Show more