Skip to content

Commit 68239b8

Browse files
committed
Fix incorrect metrics naming when group selected
1 parent ee3eeed commit 68239b8

File tree

13 files changed

+138
-144
lines changed

13 files changed

+138
-144
lines changed

web/locales/en/plugin__netobserv-plugin.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
"Scope": "Scope",
2929
"Long labels can reduce visibility.": "Long labels can reduce visibility.",
3030
"Truncate labels": "Truncate labels",
31-
"Long labels can reduce visibility.": "Long labels can reduce visibility.",
32-
"Truncate labels": "Truncate labels",
3331
"Display options": "Display options",
3432
"Source": "Source",
3533
"Destination": "Destination",
@@ -169,11 +167,11 @@
169167
"JSON": "JSON",
170168
"Kind not managed": "Kind not managed",
171169
"Unable to get flows": "Unable to get flows",
172-
"Name": "Name",
173170
"Step into this {{name}}": "Step into this {{name}}",
174171
"Add {{name}} filter": "Add {{name}} filter",
175172
"Unpin this element": "Unpin this element",
176173
"Pin this element": "Pin this element",
174+
"Name": "Name",
177175
"IP": "IP",
178176
"No information available for this content. Change scope to get more details.": "No information available for this content. Change scope to get more details.",
179177
"Source to destination:": "Source to destination:",

web/src/api/loki.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,21 @@ export interface RawTopologyMetrics {
6363
values: [number, unknown][];
6464
}
6565

66+
export interface NameAndType {
67+
name: string;
68+
type: string;
69+
}
70+
6671
export interface TopologyMetricPeer {
6772
id: string;
6873
addr?: string;
6974
namespace?: string;
70-
owner?: { name: string; type: string };
71-
resource?: { name: string; type: string };
75+
owner?: NameAndType;
76+
resource?: NameAndType;
7277
hostName?: string;
7378
resourceKind?: string;
74-
displayName?: string;
79+
isAmbiguous: boolean;
80+
getDisplayName: (inclNamespace: boolean, disambiguate: boolean) => string | undefined;
7581
}
7682

7783
export type TopologyMetrics = {

web/src/components/metrics/metrics-helper.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { NamedMetric, TopologyMetricPeer, TopologyMetrics } from '../../api/loki
55
import { MetricScope, MetricType } from '../../model/flow-query';
66
import { NodeData } from '../../model/topology';
77
import { getDateFromUnix, getFormattedDate } from '../../utils/datetime';
8-
import { getFormattedRateValue, matchPeer } from '../../utils/metrics';
8+
import { getFormattedRateValue, isUnknownPeer, matchPeer } from '../../utils/metrics';
99
import { TruncateLength } from '../dropdowns/truncate-dropdown';
1010

1111
export type LegendDataItem = {
@@ -81,10 +81,13 @@ const getPeerName = (
8181
t: TFunction,
8282
peer: TopologyMetricPeer,
8383
scope: MetricScope,
84-
truncateLength: TruncateLength = TruncateLength.OFF
84+
truncateLength: TruncateLength,
85+
inclNamespace: boolean,
86+
disambiguate: boolean
8587
): string => {
86-
if (peer.displayName) {
87-
return truncateParts(peer.displayName, truncateLength);
88+
const name = peer.getDisplayName(inclNamespace, disambiguate);
89+
if (name) {
90+
return truncateParts(name, truncateLength);
8891
}
8992
if (scope === 'app') {
9093
// No peer distinction here
@@ -104,14 +107,16 @@ export const toNamedMetric = (
104107
t: TFunction,
105108
m: TopologyMetrics,
106109
truncateLength: TruncateLength,
110+
inclNamespace: boolean,
111+
disambiguate: boolean,
107112
data?: NodeData
108113
): NamedMetric => {
109-
const srcName = getPeerName(t, m.source, m.scope, truncateLength);
110-
const srcFullName = getPeerName(t, m.source, m.scope);
111-
const dstName = getPeerName(t, m.destination, m.scope, truncateLength);
112-
const dstFullName = getPeerName(t, m.destination, m.scope);
114+
const srcName = getPeerName(t, m.source, m.scope, truncateLength, inclNamespace, disambiguate);
115+
const srcFullName = getPeerName(t, m.source, m.scope, TruncateLength.OFF, inclNamespace, disambiguate);
116+
const dstName = getPeerName(t, m.destination, m.scope, truncateLength, inclNamespace, disambiguate);
117+
const dstFullName = getPeerName(t, m.destination, m.scope, TruncateLength.OFF, inclNamespace, disambiguate);
113118
if (srcFullName === dstFullName) {
114-
if (m.source.displayName) {
119+
if (!isUnknownPeer(m.source)) {
115120
// E.g: namespace "netobserv" to "netobserv"
116121
return {
117122
...m,

web/src/components/netflow-overview/netflow-overview.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ export const NetflowOverview: React.FC<NetflowOverviewProps> = ({
100100
//limit to top X since multiple queries can run in parallel
101101
const topKMetrics = metrics
102102
.sort((a, b) => getStat(b.stats, 'sum') - getStat(a.stats, 'sum'))
103-
.map(m => toNamedMetric(t, m, truncateLength));
104-
const namedTotalMetric = toNamedMetric(t, totalMetric, truncateLength);
103+
.map(m => toNamedMetric(t, m, truncateLength, true, true));
104+
const namedTotalMetric = toNamedMetric(t, totalMetric, truncateLength, false, false);
105105
const noInternalTopK = topKMetrics.filter(m => m.source.id !== m.destination.id);
106106

107107
const smallerTexts = truncateLength >= TruncateLength.M;

web/src/components/netflow-topology/2d/topology-content.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,11 @@ export const TopologyContent: React.FC<{
164164

165165
const onFilter = React.useCallback(
166166
(data: Decorated<ElementData>) => {
167-
const isFiltered = isElementFiltered(data, filters, t);
168-
toggleElementFilter(data, isFiltered, filters, setFilters, t);
169-
setSelectedIds([data.id]);
167+
if (data.nodeType && data.peer) {
168+
const isFiltered = isElementFiltered(data.nodeType, data.peer, 'any', filters, t);
169+
toggleElementFilter(data.nodeType, data.peer, 'any', isFiltered, filters, setFilters, t);
170+
setSelectedIds([data.id]);
171+
}
170172
},
171173
[filters, setFilters, t]
172174
);

web/src/components/netflow-topology/element-field.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ export const ElementField: React.FC<{
1111
label: string;
1212
filterType: NodeType;
1313
forcedText?: string;
14-
fields: Partial<TopologyMetricPeer>;
14+
peer: TopologyMetricPeer;
1515
activeFilters: Filter[];
1616
setFilters: (filters: Filter[]) => void;
17-
}> = ({ id, label, filterType, forcedText, fields, activeFilters, setFilters }) => {
17+
}> = ({ id, label, filterType, forcedText, peer, activeFilters, setFilters }) => {
1818
return (
1919
<TextContent id={id} className="record-field-container">
2020
<Text component={TextVariants.h4}>{label}</Text>
2121
<Flex>
2222
<FlexItem flex={{ default: 'flex_1' }}>
23-
{forcedText ? <Text>{forcedText}</Text> : <PeerResourceLink fields={fields} />}
23+
{forcedText ? <Text>{forcedText}</Text> : <PeerResourceLink peer={peer} />}
2424
</FlexItem>
2525
<FlexItem>
2626
<SummaryFilterButton
2727
id={id + '-filter'}
2828
activeFilters={activeFilters}
2929
filterType={filterType}
30-
fields={fields}
30+
fields={peer}
3131
setFilters={setFilters}
3232
/>
3333
</FlexItem>

web/src/components/netflow-topology/element-fields.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
44
import { Filter } from '../../model/filters';
55
import { NodeData } from '../../model/topology';
66
import { ElementField } from './element-field';
7+
import { createPeer } from '../../utils/metrics';
78

89
export const ElementFields: React.FC<{
910
id: string;
@@ -26,7 +27,7 @@ export const ElementFields: React.FC<{
2627
forcedText={forceAsText ? data.peer.resource.name : undefined}
2728
activeFilters={activeFilters}
2829
filterType={'resource'}
29-
fields={data.peer}
30+
peer={data.peer}
3031
setFilters={setFilters}
3132
/>
3233
);
@@ -41,7 +42,7 @@ export const ElementFields: React.FC<{
4142
forcedText={forceAsText ? data.peer.owner.name : undefined}
4243
activeFilters={activeFilters}
4344
filterType={'owner'}
44-
fields={{ owner: data.peer.owner, namespace: data.peer.namespace }}
45+
peer={createPeer({ owner: data.peer.owner, namespace: data.peer.namespace })}
4546
setFilters={setFilters}
4647
/>
4748
);
@@ -56,7 +57,7 @@ export const ElementFields: React.FC<{
5657
forcedText={forceAsText ? data.peer.namespace : undefined}
5758
activeFilters={activeFilters}
5859
filterType={'namespace'}
59-
fields={{ namespace: data.peer.namespace }}
60+
peer={createPeer({ namespace: data.peer.namespace })}
6061
setFilters={setFilters}
6162
/>
6263
);
@@ -71,7 +72,7 @@ export const ElementFields: React.FC<{
7172
forcedText={forceAsText ? data.peer.hostName : undefined}
7273
activeFilters={activeFilters}
7374
filterType={'host'}
74-
fields={{ hostName: data.peer.hostName }}
75+
peer={createPeer({ hostName: data.peer.hostName })}
7576
setFilters={setFilters}
7677
/>
7778
);
@@ -85,7 +86,7 @@ export const ElementFields: React.FC<{
8586
label={t('IP')}
8687
activeFilters={activeFilters}
8788
filterType={'resource'}
88-
fields={{ addr: data.peer.addr }}
89+
peer={createPeer({ addr: data.peer.addr })}
8990
setFilters={setFilters}
9091
/>
9192
);

web/src/components/netflow-topology/element-panel.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ export const ElementPanelMetricsContent: React.FC<{
166166
);
167167

168168
if (element instanceof BaseNode && data) {
169-
// TODO: fix metrics naming when a group is selected (ambiguous)
169+
const leafData = element.getType() === 'group' ? undefined : data;
170170
const filteredMetrics = metrics.filter(m => m.source.id !== m.destination.id);
171171
const outMetrics = filteredMetrics
172172
.filter(m => matchPeer(data, m.source))
173-
.map(m => toNamedMetric(t, m, truncateLength, data));
173+
.map(m => toNamedMetric(t, m, truncateLength, false, false, leafData));
174174
const inMetrics = filteredMetrics
175175
.filter(m => matchPeer(data, m.destination))
176-
.map(m => toNamedMetric(t, m, truncateLength, data));
176+
.map(m => toNamedMetric(t, m, truncateLength, false, false, leafData));
177177
const outCount = outMetrics.reduce((prev, cur) => prev + getStat(cur.stats, metricFunction), 0);
178178
const inCount = inMetrics.reduce((prev, cur) => prev + getStat(cur.stats, metricFunction), 0);
179179
return (
@@ -198,10 +198,10 @@ export const ElementPanelMetricsContent: React.FC<{
198198
const bData = element.getTarget().getData();
199199
const aToBMetrics = metrics
200200
.filter(m => matchPeer(aData, m.source) && matchPeer(bData, m.destination))
201-
.map(m => toNamedMetric(t, m, truncateLength));
201+
.map(m => toNamedMetric(t, m, truncateLength, false, false));
202202
const bToAMetrics = metrics
203203
.filter(m => matchPeer(bData, m.source) && matchPeer(aData, m.destination))
204-
.map(m => toNamedMetric(t, m, truncateLength));
204+
.map(m => toNamedMetric(t, m, truncateLength, false, false));
205205
const aToBCount = aToBMetrics.reduce((prev, cur) => prev + getStat(cur.stats, metricFunction), 0);
206206
const bToACount = bToAMetrics.reduce((prev, cur) => prev + getStat(cur.stats, metricFunction), 0);
207207
return (
@@ -243,7 +243,7 @@ export const ElementPanel: React.FC<{
243243
return <Text component={TextVariants.h2}>{t('Edge')}</Text>;
244244
} else {
245245
const data = element.getData();
246-
return <>{data && <PeerResourceLink fields={data.peer} />}</>;
246+
return <>{data && <PeerResourceLink peer={data.peer} />}</>;
247247
}
248248
}, [element, t]);
249249

web/src/components/netflow-topology/peer-resource-link.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import * as React from 'react';
22
import { ResourceLink } from '@openshift-console/dynamic-plugin-sdk';
33
import { Text } from '@patternfly/react-core';
44
import { TopologyMetricPeer } from '../../api/loki';
5-
import { peerNameAndKind } from '../../utils/metrics';
65

7-
export const PeerResourceLink: React.FC<{ fields: Partial<TopologyMetricPeer> }> = ({ fields }) => {
8-
const nk = peerNameAndKind(fields, false);
9-
if (nk) {
10-
if (nk.kind) {
11-
return <ResourceLink inline={true} kind={nk.kind} name={nk.name} namespace={fields.namespace} />;
6+
export const PeerResourceLink: React.FC<{ peer: TopologyMetricPeer }> = ({ peer }) => {
7+
const name = peer.getDisplayName(false, false);
8+
if (name) {
9+
if (peer.resourceKind) {
10+
return <ResourceLink inline={true} kind={peer.resourceKind} name={name} namespace={peer.namespace} />;
1211
} else {
13-
return <Text>{nk.name}</Text>;
12+
return <Text>{name}</Text>;
1413
}
1514
}
1615
return null;

web/src/model/topology.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { K8sModel } from '@openshift-console/dynamic-plugin-sdk';
2222
import { getTopologyEdgeId } from '../utils/ids';
2323
import { MetricScopeOptions } from './metrics';
2424
import { MetricFunction, MetricScope, MetricType, NodeType } from './flow-query';
25-
import { createPeer, getFormattedValue, peerNameAndKind } from '../utils/metrics';
25+
import { createPeer, getFormattedValue } from '../utils/metrics';
2626
import { TruncateLength } from '../components/dropdowns/truncate-dropdown';
2727

2828
export enum LayoutName {
@@ -211,10 +211,8 @@ const generateNode = (
211211
k8sModels: { [key: string]: K8sModel },
212212
isDark?: boolean
213213
): NodeModel => {
214-
const nk = peerNameAndKind(data.peer, false);
215-
const label = nk?.name || (scope === 'host' ? t('External') : t('Unknown'))!;
216-
data.peer.displayName = label;
217-
const resourceKind = nk?.kind;
214+
const label = data.peer.getDisplayName(false, false) || (scope === 'host' ? t('External') : t('Unknown'))!;
215+
const resourceKind = data.peer.resourceKind;
218216
const secondaryLabel =
219217
data.nodeType !== 'namespace' &&
220218
![
@@ -359,12 +357,13 @@ export const generateDataModel = (
359357
const opts = { ...DefaultOptions, ...options };
360358

361359
const addGroup = (
362-
metricFields: Omit<TopologyMetricPeer, 'id'>,
360+
fields: Partial<TopologyMetricPeer>,
363361
scope: MetricScope,
364362
parent?: NodeModel,
365363
secondaryLabelPadding = false
366364
): NodeModel => {
367-
const groupDef = createPeer(metricFields);
365+
const groupDef = createPeer(fields);
366+
const groupName = groupDef.getDisplayName(false, false);
368367
let group = nodes.find(g => g.type === 'group' && g.id === groupDef.id);
369368
if (!group) {
370369
const data: NodeData = {
@@ -377,11 +376,11 @@ export const generateDataModel = (
377376
type: 'group',
378377
group: true,
379378
collapsed: options.startCollapsed,
380-
label: groupDef.displayName,
379+
label: groupName,
381380
style: { padding: secondaryLabelPadding ? 35 : 10 },
382381
data: {
383382
...data,
384-
name: groupDef.displayName,
383+
name: groupName,
385384
isDark,
386385
labelPosition: LabelPosition.bottom,
387386
collapsible: true,
@@ -488,22 +487,27 @@ export const generateDataModel = (
488487
return ownerGroup || namespaceGroup || hostGroup;
489488
};
490489

491-
const dataBuilder: (p: TopologyMetricPeer) => NodeData =
492-
metricScope === 'host'
493-
? p =>
494-
_.isEmpty(p.hostName) ? { peer: p, nodeType: 'unknown' } : { peer: p, nodeType: 'host', canStepInto: true }
495-
: metricScope === 'namespace'
496-
? p =>
497-
_.isEmpty(p.namespace)
498-
? { peer: p, nodeType: 'unknown' }
499-
: { peer: p, nodeType: 'namespace', canStepInto: true }
500-
: metricScope === 'owner'
501-
? p => (p.owner ? { peer: p, nodeType: 'owner', canStepInto: true } : { peer: p, nodeType: 'unknown' })
502-
: p => ({ peer: p, nodeType: 'resource' });
490+
const peerToNodeData = (p: TopologyMetricPeer): NodeData => {
491+
switch (metricScope) {
492+
case 'host':
493+
return _.isEmpty(p.hostName)
494+
? { peer: p, nodeType: 'unknown' }
495+
: { peer: p, nodeType: 'host', canStepInto: true };
496+
case 'namespace':
497+
return _.isEmpty(p.namespace)
498+
? { peer: p, nodeType: 'unknown' }
499+
: { peer: p, nodeType: 'namespace', canStepInto: true };
500+
case 'owner':
501+
return p.owner ? { peer: p, nodeType: 'owner', canStepInto: true } : { peer: p, nodeType: 'unknown' };
502+
case 'resource':
503+
default:
504+
return { peer: p, nodeType: 'resource' };
505+
}
506+
};
503507

504508
metrics.forEach(m => {
505-
const srcNode = addNode(dataBuilder(m.source), metricScope);
506-
const dstNode = addNode(dataBuilder(m.destination), metricScope);
509+
const srcNode = addNode(peerToNodeData(m.source), metricScope);
510+
const dstNode = addNode(peerToNodeData(m.destination), metricScope);
507511

508512
if (options.edges && srcNode && dstNode && srcNode.id !== dstNode.id) {
509513
addEdge(

0 commit comments

Comments
 (0)