Skip to content

Commit 520f3ff

Browse files
authored
NETOBSERV-1720: fix issues with topology scopes (#550)
Fixes also NETOBSERV-1721 and NETOBSERV-1722 - new func getStepIntoNext that provides the next scope for 'stepInto' and cares about the allowed scopes - fix call to toggleDirElementFilter on stepInto (it was *removing* the filter instead of adding it), and prevent adding duplicate filters - Do not link groups invalidation to scope via useEffect, as it results in doing 2 queries (first with the invalid group, then without). Make them more strictly tied instead.
1 parent 56ada60 commit 520f3ff

File tree

4 files changed

+79
-36
lines changed

4 files changed

+79
-36
lines changed

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

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import {
2727
ElementData,
2828
FilterDir,
2929
generateDataModel,
30+
getStepIntoNext,
3031
GraphElementPeer,
32+
isDirElementFiltered,
3133
LayoutName,
3234
NodeData,
3335
toggleDirElementFilter,
@@ -55,6 +57,7 @@ export interface TopologyContentProps {
5557
metricType: MetricType;
5658
metricScope: FlowScope;
5759
setMetricScope: (ms: FlowScope) => void;
60+
allowedScopes: FlowScope[];
5861
metrics: TopologyMetrics[];
5962
droppedMetrics: TopologyMetrics[];
6063
options: TopologyOptions;
@@ -75,6 +78,7 @@ export const TopologyContent: React.FC<TopologyContentProps> = ({
7578
metricType,
7679
metricScope,
7780
setMetricScope,
81+
allowedScopes,
7882
metrics,
7983
droppedMetrics,
8084
options,
@@ -195,51 +199,58 @@ export const TopologyContent: React.FC<TopologyContentProps> = ({
195199

196200
const onStepInto = React.useCallback(
197201
(data: Decorated<ElementData>) => {
198-
let scope: MetricScopeOptions;
199202
let groupTypes: TopologyGroupTypes;
200203
switch (metricScope) {
201204
case MetricScopeOptions.CLUSTER:
202-
scope = MetricScopeOptions.ZONE;
203205
groupTypes = TopologyGroupTypes.clusters;
204206
break;
205207
case MetricScopeOptions.ZONE:
206-
scope = MetricScopeOptions.HOST;
207208
groupTypes = TopologyGroupTypes.zones;
208209
break;
209210
case MetricScopeOptions.HOST:
210-
scope = MetricScopeOptions.NAMESPACE;
211-
groupTypes = TopologyGroupTypes.none;
211+
groupTypes = TopologyGroupTypes.hosts;
212212
break;
213213
case MetricScopeOptions.NAMESPACE:
214-
scope = MetricScopeOptions.OWNER;
215214
groupTypes = TopologyGroupTypes.namespaces;
216215
break;
217216
default:
218-
scope = MetricScopeOptions.RESOURCE;
219217
groupTypes = TopologyGroupTypes.owners;
220218
}
221-
if (data.nodeType && data.peer) {
219+
const scope = getStepIntoNext(metricScope, allowedScopes);
220+
if (data.nodeType && data.peer && scope) {
222221
setMetricScope(scope);
223222
setOptions({ ...options, groupTypes });
224-
toggleDirElementFilter(
225-
data.nodeType,
226-
data.peer,
227-
'src',
228-
true,
229-
filters.list,
230-
list => {
231-
setFilters({ list: list, backAndForth: true });
232-
},
233-
filterDefinitions
234-
);
223+
if (!isDirElementFiltered(data.nodeType, data.peer, 'src', filters.list, filterDefinitions)) {
224+
toggleDirElementFilter(
225+
data.nodeType,
226+
data.peer,
227+
'src',
228+
false,
229+
filters.list,
230+
list => {
231+
setFilters({ list: list, backAndForth: true });
232+
},
233+
filterDefinitions
234+
);
235+
}
235236
setSelectedIds([data.id]);
236237
//clear search
237238
onChangeSearch();
238239
//clear selection
239240
onSelect(undefined);
240241
}
241242
},
242-
[metricScope, setMetricScope, setOptions, options, filters.list, filterDefinitions, onSelect, setFilters]
243+
[
244+
metricScope,
245+
setMetricScope,
246+
allowedScopes,
247+
setOptions,
248+
options,
249+
filters.list,
250+
filterDefinitions,
251+
onSelect,
252+
setFilters
253+
]
243254
);
244255

245256
const onHover = React.useCallback((data: Decorated<ElementData>) => {
@@ -344,6 +355,7 @@ export const TopologyContent: React.FC<TopologyContentProps> = ({
344355
droppedMetrics,
345356
getOptions(),
346357
metricScope,
358+
allowedScopes,
347359
searchEvent?.searchValue || '',
348360
highlightedId,
349361
filters,
@@ -386,6 +398,7 @@ export const TopologyContent: React.FC<TopologyContentProps> = ({
386398
selectedIds,
387399
getOptions,
388400
metricScope,
401+
allowedScopes,
389402
searchEvent?.searchValue,
390403
filters,
391404
t,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export const NetflowTopology: React.FC<NetflowTopologyProps> = ({
109109
metricType={metricType}
110110
metricScope={metricScope}
111111
setMetricScope={setMetricScope}
112+
allowedScopes={allowedScopes}
112113
metrics={displayedMetrics}
113114
droppedMetrics={droppedMetrics}
114115
options={options}

web/src/components/netflow-traffic.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export const NetflowTraffic: React.FC<NetflowTrafficProps> = ({ forcedFilters, i
242242
const [lastTop, setLastTop] = useLocalStorage<number>(localStorageLastTopKey, topValues[0]);
243243
const [range, setRange] = React.useState<number | TimeRange>(getRangeFromURL());
244244
const [histogramRange, setHistogramRange] = React.useState<TimeRange>();
245-
const [metricScope, setMetricScope] = useLocalStorage<FlowScope>(localStorageMetricScopeKey, 'namespace');
245+
const [metricScope, _setMetricScope] = useLocalStorage<FlowScope>(localStorageMetricScopeKey, 'namespace');
246246
const [topologyMetricFunction, setTopologyMetricFunction] = useLocalStorage<StatFunction>(
247247
localStorageMetricFunctionKey,
248248
defaultMetricFunction
@@ -435,6 +435,18 @@ export const NetflowTraffic: React.FC<NetflowTrafficProps> = ({ forcedFilters, i
435435
[config, getQuickFilters]
436436
);
437437

438+
const setMetricScope = React.useCallback(
439+
(scope: FlowScope) => {
440+
_setMetricScope(scope);
441+
// Invalidate groups if necessary, when metrics scope changed
442+
const groups = getGroupsForScope(scope as MetricScopeOptions);
443+
if (!groups.includes(topologyOptions.groupTypes)) {
444+
setTopologyOptions({ ...topologyOptions, groupTypes: TopologyGroupTypes.none });
445+
}
446+
},
447+
[_setMetricScope, topologyOptions, setTopologyOptions]
448+
);
449+
438450
const getTopologyMetrics = React.useCallback(() => {
439451
switch (topologyMetricType) {
440452
case 'Bytes':
@@ -1261,14 +1273,6 @@ export const NetflowTraffic: React.FC<NetflowTrafficProps> = ({ forcedFilters, i
12611273
// eslint-disable-next-line react-hooks/exhaustive-deps
12621274
}, [filters]);
12631275

1264-
//invalidate groups if necessary, when metrics scope changed
1265-
React.useEffect(() => {
1266-
const groups = getGroupsForScope(metricScope as MetricScopeOptions);
1267-
if (!groups.includes(topologyOptions.groupTypes)) {
1268-
setTopologyOptions({ ...topologyOptions, groupTypes: TopologyGroupTypes.none });
1269-
}
1270-
}, [metricScope, topologyOptions, setTopologyOptions]);
1271-
12721276
const clearFilters = React.useCallback(() => {
12731277
if (forcedFilters) {
12741278
navigate(netflowTrafficPath);

web/src/model/topology.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,31 @@ export const isGroupEnabled = (group: TopologyGroupTypes, enabledScopes: FlowSco
102102
);
103103
};
104104

105+
export const getStepIntoNext = (current: FlowScope, allowedScopes: FlowScope[]): FlowScope | undefined => {
106+
let next: FlowScope | undefined = undefined;
107+
switch (current) {
108+
case 'cluster':
109+
next = 'zone';
110+
break;
111+
case 'zone':
112+
next = 'host';
113+
break;
114+
case 'host':
115+
next = 'resource';
116+
break;
117+
case 'namespace':
118+
next = 'owner';
119+
break;
120+
case 'owner':
121+
next = 'resource';
122+
break;
123+
}
124+
if (!next || allowedScopes.includes(next)) {
125+
return next;
126+
}
127+
return getStepIntoNext(next, allowedScopes);
128+
};
129+
105130
export interface TopologyOptions {
106131
maxEdgeStat: number;
107132
nodeBadges?: boolean;
@@ -456,6 +481,7 @@ export const generateDataModel = (
456481
droppedMetrics: TopologyMetrics[],
457482
options: TopologyOptions,
458483
metricScope: FlowScope,
484+
allowedScopes: FlowScope[],
459485
searchValue: string,
460486
highlightedId: string,
461487
filters: Filters,
@@ -656,23 +682,22 @@ export const generateDataModel = (
656682
};
657683

658684
const peerToNodeData = (p: TopologyMetricPeer): NodeData => {
685+
const canStepInto = getStepIntoNext(metricScope, allowedScopes) !== undefined;
659686
switch (metricScope) {
660687
case 'cluster':
661688
return _.isEmpty(p.clusterName)
662689
? { peer: p, nodeType: 'unknown' }
663-
: { peer: p, nodeType: 'cluster', canStepInto: true };
690+
: { peer: p, nodeType: 'cluster', canStepInto };
664691
case 'zone':
665-
return _.isEmpty(p.zone) ? { peer: p, nodeType: 'unknown' } : { peer: p, nodeType: 'zone', canStepInto: true };
692+
return _.isEmpty(p.zone) ? { peer: p, nodeType: 'unknown' } : { peer: p, nodeType: 'zone', canStepInto };
666693
case 'host':
667-
return _.isEmpty(p.hostName)
668-
? { peer: p, nodeType: 'unknown' }
669-
: { peer: p, nodeType: 'host', canStepInto: true };
694+
return _.isEmpty(p.hostName) ? { peer: p, nodeType: 'unknown' } : { peer: p, nodeType: 'host', canStepInto };
670695
case 'namespace':
671696
return _.isEmpty(p.namespace)
672697
? { peer: p, nodeType: 'unknown' }
673-
: { peer: p, nodeType: 'namespace', canStepInto: true };
698+
: { peer: p, nodeType: 'namespace', canStepInto };
674699
case 'owner':
675-
return p.owner ? { peer: p, nodeType: 'owner', canStepInto: true } : { peer: p, nodeType: 'unknown' };
700+
return p.owner ? { peer: p, nodeType: 'owner', canStepInto } : { peer: p, nodeType: 'unknown' };
676701
case 'resource':
677702
default:
678703
return { peer: p, nodeType: 'resource' };

0 commit comments

Comments
 (0)