Skip to content

Commit

Permalink
[Metrics UI] Add metadata tab to node details flyout (elastic#84454)
Browse files Browse the repository at this point in the history
* Add properties tab to flyout

* Better id for i18n title

* Update i18n ids

* Fix test and styling

* Style changes, add support for collapsing array fields

* Add loading indicators

* Fix type check

* Fix another test

* Fix tests for pods

* Add link to node details page

* Only show the overlay when viewing hosts

* Take into account cores when showing cpu

* Make it easier to read

* Remove unnecessary cast

* Fix PR feedback

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
phillipb and kibanamachine committed Dec 3, 2020
1 parent 1ab0cbe commit e3e3c40
Show file tree
Hide file tree
Showing 11 changed files with 538 additions and 39 deletions.
20 changes: 20 additions & 0 deletions x-pack/plugins/infra/common/http_api/metadata_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ export const InfraMetadataOSRT = rt.partial({
name: rt.string,
platform: rt.string,
version: rt.string,
build: rt.string,
});

export const InfraMetadataHostRT = rt.partial({
name: rt.string,
hostname: rt.string,
id: rt.string,
ip: rt.array(rt.string),
mac: rt.array(rt.string),
os: InfraMetadataOSRT,
architecture: rt.string,
containerized: rt.boolean,
Expand All @@ -43,25 +48,40 @@ export const InfraMetadataInstanceRT = rt.partial({
name: rt.string,
});

export const InfraMetadataAccountRT = rt.partial({
id: rt.string,
name: rt.string,
});

export const InfraMetadataProjectRT = rt.partial({
id: rt.string,
});

export const InfraMetadataMachineRT = rt.partial({
interface: rt.string,
type: rt.string,
});

export const InfraMetadataCloudRT = rt.partial({
instance: InfraMetadataInstanceRT,
provider: rt.string,
account: InfraMetadataAccountRT,
availability_zone: rt.string,
project: InfraMetadataProjectRT,
machine: InfraMetadataMachineRT,
region: rt.string,
});

export const InfraMetadataAgentRT = rt.partial({
id: rt.string,
version: rt.string,
policy: rt.string,
});

export const InfraMetadataInfoRT = rt.partial({
cloud: InfraMetadataCloudRT,
host: InfraMetadataHostRT,
agent: InfraMetadataAgentRT,
});

const InfraMetadataRequiredRT = rt.type({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import { InventoryItemType } from '../../../../../../common/inventory_models/typ
import { MetricsTab } from './tabs/metrics/metrics';
import { LogsTab } from './tabs/logs';
import { ProcessesTab } from './tabs/processes';
import { PropertiesTab } from './tabs/properties';
import { PropertiesTab } from './tabs/properties/index';
import { OVERLAY_Y_START, OVERLAY_BOTTOM_MARGIN, OVERLAY_HEADER_SIZE } from './tabs/shared';
import { useLinkProps } from '../../../../../hooks/use_link_props';
import { getNodeDetailUrl } from '../../../../link_to';
import { findInventoryModel } from '../../../../../../common/inventory_models';

interface Props {
isOpen: boolean;
Expand All @@ -35,6 +38,8 @@ export const NodeContextPopover = ({
}: Props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const tabConfigs = [MetricsTab, LogsTab, ProcessesTab, PropertiesTab];
const inventoryModel = findInventoryModel(nodeType);
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;

const tabs = useMemo(() => {
return tabConfigs.map((m) => {
Expand All @@ -50,6 +55,15 @@ export const NodeContextPopover = ({

const [selectedTab, setSelectedTab] = useState(0);

const nodeDetailMenuItemLinkProps = useLinkProps({
...getNodeDetailUrl({
nodeType,
nodeId: node.id,
from: nodeDetailFrom,
to: currentTime,
}),
});

if (!isOpen) {
return null;
}
Expand All @@ -65,9 +79,28 @@ export const NodeContextPopover = ({
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={onClose} iconType={'cross'}>
<FormattedMessage id="xpack.infra.infra.nodeDetails.close" defaultMessage="Close" />
</EuiButtonEmpty>
<EuiFlexGroup gutterSize={'xs'} alignItems={'flexEnd'}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconSide={'left'}
iconType={'popout'}
href={nodeDetailMenuItemLinkProps.href}
>
<FormattedMessage
id="xpack.infra.infra.nodeDetails.openAsPage"
defaultMessage="Open as page"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={onClose} iconType={'cross'}>
<FormattedMessage
id="xpack.infra.infra.nodeDetails.close"
defaultMessage="Close"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</OverlayHeaderTitleWrapper>
<EuiTabs>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
PointerEvent,
} from '@elastic/charts';
import moment from 'moment';
import { EuiLoadingChart } from '@elastic/eui';
import { TabContent, TabProps } from '../shared';
import { useSnapshot } from '../../../../hooks/use_snaphot';
import { useWaffleOptionsContext } from '../../../../hooks/use_waffle_options';
Expand Down Expand Up @@ -82,9 +83,9 @@ const TabComponent = (props: TabProps) => {
}

const buildCustomMetric = useCallback(
(field: string, id: string) => ({
(field: string, id: string, aggregation: string = 'avg') => ({
type: 'custom' as SnapshotMetricType,
aggregation: 'avg',
aggregation,
field,
id,
}),
Expand All @@ -110,6 +111,7 @@ const TabComponent = (props: TabProps) => {
buildCustomMetric('system.load.15', 'load15m'),
buildCustomMetric('system.memory.actual.used.bytes', 'usedMemory'),
buildCustomMetric('system.memory.actual.free', 'freeMemory'),
buildCustomMetric('system.cpu.cores', 'cores', 'max'),
],
[],
nodeType,
Expand Down Expand Up @@ -223,6 +225,7 @@ const TabComponent = (props: TabProps) => {
const load15mMetricsTs = useMemo(() => getTimeseries('load15m'), [getTimeseries]);
const usedMemoryMetricsTs = useMemo(() => getTimeseries('usedMemory'), [getTimeseries]);
const freeMemoryMetricsTs = useMemo(() => getTimeseries('freeMemory'), [getTimeseries]);
const coresMetricsTs = useMemo(() => getTimeseries('cores'), [getTimeseries]);

useEffect(() => {
reload();
Expand All @@ -239,7 +242,7 @@ const TabComponent = (props: TabProps) => {
!usedMemoryMetricsTs ||
!freeMemoryMetricsTs
) {
return <div />;
return <LoadingPlaceholder />;
}

const cpuChartMetrics = buildChartMetricLabels([SYSTEM_METRIC_NAME, USER_METRIC_NAME], 'avg');
Expand All @@ -253,6 +256,23 @@ const TabComponent = (props: TabProps) => {
'rate'
);

systemMetricsTs.rows = systemMetricsTs.rows.slice().map((r, idx) => {
const metric = r.metric_0 as number | undefined;
const cores = coresMetricsTs!.rows[idx].metric_0 as number | undefined;
if (metric && cores) {
r.metric_0 = metric / cores;
}
return r;
});

userMetricsTs.rows = userMetricsTs.rows.slice().map((r, idx) => {
const metric = r.metric_0 as number | undefined;
const cores = coresMetricsTs!.rows[idx].metric_0 as number | undefined;
if (metric && cores) {
r.metric_0 = metric / cores;
}
return r;
});
const cpuTimeseries = mergeTimeseries(systemMetricsTs, userMetricsTs);
const networkTimeseries = mergeTimeseries(rxMetricsTs, txMetricsTs);
const loadTimeseries = mergeTimeseries(load1mMetricsTs, load5mMetricsTs, load15mMetricsTs);
Expand Down Expand Up @@ -467,6 +487,23 @@ const ChartContainer: React.FC = ({ children }) => (
</div>
);

const LoadingPlaceholder = () => {
return (
<div
style={{
width: '100%',
height: '100%',
padding: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<EuiLoadingChart size="xl" />
</div>
);
};

export const MetricsTab = {
id: 'metrics',
name: i18n.translate('xpack.infra.nodeDetails.tabs.metrics', {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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 { InfraMetadata } from '../../../../../../../../common/http_api';

export const getFields = (metadata: InfraMetadata, group: 'cloud' | 'host' | 'agent') => {
switch (group) {
case 'host':
return prune([
{
name: 'host.architecture',
value: metadata.info?.host?.architecture,
},
{
name: 'host.hostname',
value: metadata.info?.host?.name,
},
{
name: 'host.id',
value: metadata.info?.host?.id,
},
{
name: 'host.ip',
value: metadata.info?.host?.ip,
},
{
name: 'host.mac',
value: metadata.info?.host?.mac,
},
{
name: 'host.name',
value: metadata.info?.host?.name,
},
{
name: 'host.os.build',
value: metadata.info?.host?.os?.build,
},
{
name: 'host.os.family',
value: metadata.info?.host?.os?.family,
},
{
name: 'host.os.name',
value: metadata.info?.host?.os?.name,
},
{
name: 'host.os.kernel',
value: metadata.info?.host?.os?.kernel,
},
{
name: 'host.os.platform',
value: metadata.info?.host?.os?.platform,
},
{
name: 'host.os.version',
value: metadata.info?.host?.os?.version,
},
]);
case 'cloud':
return prune([
{
name: 'cloud.account.id',
value: metadata.info?.cloud?.account?.id,
},
{
name: 'cloud.account.name',
value: metadata.info?.cloud?.account?.name,
},
{
name: 'cloud.availability_zone',
value: metadata.info?.cloud?.availability_zone,
},
{
name: 'cloud.instance.id',
value: metadata.info?.cloud?.instance?.id,
},
{
name: 'cloud.instance.name',
value: metadata.info?.cloud?.instance?.name,
},
{
name: 'cloud.machine.type',
value: metadata.info?.cloud?.machine?.type,
},
{
name: 'cloud.provider',
value: metadata.info?.cloud?.provider,
},
{
name: 'cloud.region',
value: metadata.info?.cloud?.region,
},
]);
case 'agent':
return prune([
{
name: 'agent.id',
value: metadata.info?.agent?.id,
},
{
name: 'agent.version',
value: metadata.info?.agent?.version,
},
{
name: 'agent.policy',
value: metadata.info?.agent?.policy,
},
]);
}
};

const prune = (fields: Array<{ name: string; value: string | string[] | undefined }>) =>
fields.filter((f) => !!f.value);
Loading

0 comments on commit e3e3c40

Please sign in to comment.