Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

NETOBSERV-1113 implements RTT option in Console Plugin #365

Merged
merged 7 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/sample-frontend-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ sampling: 50
features:
- pktDrop
- dnsTracking
- flowRTT
4 changes: 2 additions & 2 deletions mocks/loki/flows.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
"values": [
[
"1689619351078000128",
"{\"Etype\":2048,\"SrcK8S_HostName\":\"ci-ln-hnd9rjk-72292-hnd5v-master-0\",\"Duplicate\":false,\"Interface\":\"ens4\",\"DstAddr\":\"10.0.0.3\",\"DstK8S_HostIP\":\"10.0.0.3\",\"SrcAddr\":\"10.0.0.5\",\"SrcK8S_Type\":\"Node\",\"SrcK8S_OwnerType\":\"Node\",\"Proto\":6,\"DstPort\":2380,\"Packets\":1,\"AgentIP\":\"10.0.0.5\",\"Bytes\":66,\"SrcPort\":49834,\"DstK8S_Type\":\"Node\",\"SrcMac\":\"42:01:0A:00:00:05\",\"TimeFlowEndMs\":1689619351078,\"TimeFlowStartMs\":1689619351078,\"SrcK8S_HostIP\":\"10.0.0.5\",\"DstK8S_OwnerType\":\"Node\",\"Flags\":16,\"DstMac\":\"42:01:0A:00:00:01\",\"DstK8S_HostName\":\"ci-ln-hnd9rjk-72292-hnd5v-master-2\",\"TimeReceived\":1689619351,\"DstK8S_Name\":\"ci-ln-hnd9rjk-72292-hnd5v-master-2\",\"SrcK8S_Name\":\"ci-ln-hnd9rjk-72292-hnd5v-master-0\",\"IfDirection\":1}"
"{\"Etype\":2048,\"SrcK8S_HostName\":\"ci-ln-hnd9rjk-72292-hnd5v-master-0\",\"Duplicate\":false,\"Interface\":\"ens4\",\"DstAddr\":\"10.0.0.3\",\"DstK8S_HostIP\":\"10.0.0.3\",\"SrcAddr\":\"10.0.0.5\",\"SrcK8S_Type\":\"Node\",\"SrcK8S_OwnerType\":\"Node\",\"Proto\":6,\"DstPort\":2380,\"Packets\":1,\"AgentIP\":\"10.0.0.5\",\"Bytes\":66,\"SrcPort\":49834,\"DstK8S_Type\":\"Node\",\"SrcMac\":\"42:01:0A:00:00:05\",\"TimeFlowEndMs\":1689619351078,\"TimeFlowRttNs\":12345678,\"TimeFlowStartMs\":1689619351078,\"SrcK8S_HostIP\":\"10.0.0.5\",\"DstK8S_OwnerType\":\"Node\",\"Flags\":16,\"DstMac\":\"42:01:0A:00:00:01\",\"DstK8S_HostName\":\"ci-ln-hnd9rjk-72292-hnd5v-master-2\",\"TimeReceived\":1689619351,\"DstK8S_Name\":\"ci-ln-hnd9rjk-72292-hnd5v-master-2\",\"SrcK8S_Name\":\"ci-ln-hnd9rjk-72292-hnd5v-master-0\",\"IfDirection\":1}"
],
[
"1689619351032000000",
Expand Down Expand Up @@ -308,7 +308,7 @@
"values": [
[
"1689619351079000064",
"{\"SrcMac\":\"42:01:0A:00:00:01\",\"DstPort\":6443,\"SrcPort\":54082,\"Etype\":2048,\"SrcK8S_Type\":\"Node\",\"AgentIP\":\"10.0.0.5\",\"Bytes\":66,\"Packets\":1,\"SrcK8S_HostIP\":\"10.0.0.4\",\"SrcK8S_HostName\":\"ci-ln-hnd9rjk-72292-hnd5v-master-1\",\"SrcK8S_OwnerType\":\"Node\",\"Proto\":6,\"Flags\":16,\"SrcAddr\":\"10.0.0.4\",\"SrcK8S_Name\":\"ci-ln-hnd9rjk-72292-hnd5v-master-1\",\"DstMac\":\"42:01:0A:00:00:05\",\"TimeFlowStartMs\":1689619351079,\"Duplicate\":false,\"IfDirection\":0,\"TimeFlowEndMs\":1689619351079,\"TimeReceived\":1689619351,\"DstAddr\":\"10.0.0.2\",\"Interface\":\"br-ex\"}"
"{\"SrcMac\":\"42:01:0A:00:00:01\",\"DstPort\":6443,\"SrcPort\":54082,\"Etype\":2048,\"SrcK8S_Type\":\"Node\",\"AgentIP\":\"10.0.0.5\",\"Bytes\":66,\"Packets\":1,\"SrcK8S_HostIP\":\"10.0.0.4\",\"SrcK8S_HostName\":\"ci-ln-hnd9rjk-72292-hnd5v-master-1\",\"SrcK8S_OwnerType\":\"Node\",\"Proto\":6,\"Flags\":16,\"SrcAddr\":\"10.0.0.4\",\"SrcK8S_Name\":\"ci-ln-hnd9rjk-72292-hnd5v-master-1\",\"DstMac\":\"42:01:0A:00:00:05\",\"TimeFlowStartMs\":1689619351079,\"Duplicate\":false,\"IfDirection\":0,\"TimeFlowEndMs\":1689619351079,\"TimeFlowRttNs\":1234,\"TimeReceived\":1689619351,\"DstAddr\":\"10.0.0.2\",\"Interface\":\"br-ex\"}"
],
[
"1689619350793999872",
Expand Down
3 changes: 2 additions & 1 deletion pkg/handler/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ func getMetricType(params url.Values) (constants.MetricType, error) {
metricType == constants.MetricTypeDroppedPackets ||
metricType == constants.MetricTypePackets ||
metricType == constants.MetricTypeDNSLatencies ||
metricType == constants.MetricTypeCountDNS {
metricType == constants.MetricTypeCountDNS ||
metricType == constants.MetricTypeFlowRTT {
return metricType, nil
}
return "", fmt.Errorf("invalid metric type: %s", mt)
Expand Down
2 changes: 1 addition & 1 deletion pkg/loki/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func moreThanRegex(sb *strings.Builder, value string) {
intVal, _ := strconv.Atoi(value)
for i := 1; i < len(value); i++ {
nextMin := int((intVal / int(math.Pow10(i))) + 1)
nextMinStr := fmt.Sprintf("%d", nextMin)
nextMinStr := strconv.Itoa(nextMin)

sb.WriteRune('|')
if nextMin >= 10 {
Expand Down
8 changes: 8 additions & 0 deletions pkg/loki/flow_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ func (q *FlowQueryBuilder) appendDNSRCodeFilter(sb *strings.Builder) {
sb.WriteString("`")
}

func (q *FlowQueryBuilder) appendRTTFilter(sb *strings.Builder) {
// ensure at TimeFlowRttNs field is specified
// |~`"TimeFlowRttNs"`
sb.WriteString("|~`")
sb.WriteString(`"TimeFlowRttNs"`)
sb.WriteString("`")
}

func (q *FlowQueryBuilder) appendJSON(sb *strings.Builder, forceAppend bool) {
if forceAppend || len(q.jsonFilters) > 0 {
sb.WriteString("|json")
Expand Down
23 changes: 20 additions & 3 deletions pkg/loki/topology_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type Topology struct {
skipEmptyDropCause bool
skipNonDNS bool
skipEmptyDNSRCode bool
skipEmptyRTT bool
factor string
}

type TopologyQueryBuilder struct {
Expand All @@ -39,6 +41,7 @@ func NewTopologyQuery(cfg *Config, start, end, limit, rateInterval, step string,

fields := getFields(aggregate, groups)
var f, t string
factor := ""
switch metricType {
case constants.MetricTypeCount, constants.MetricTypeCountDNS:
f = "count_over_time"
Expand All @@ -57,6 +60,10 @@ func NewTopologyQuery(cfg *Config, start, end, limit, rateInterval, step string,
case constants.MetricTypeBytes:
f = "rate"
t = "Bytes"
case constants.MetricTypeFlowRTT:
f = "avg_over_time"
t = "TimeFlowRttNs"
factor = "/1000000" // nanoseconds to miliseconds
}

var d bool
Expand All @@ -80,8 +87,10 @@ func NewTopologyQuery(cfg *Config, start, end, limit, rateInterval, step string,
fields: fields,
skipEmptyDropState: aggregate == "droppedState",
skipEmptyDropCause: aggregate == "droppedCause",
skipNonDNS: metricType == "dnsLatencies" || metricType == "countDns",
skipNonDNS: metricType == constants.MetricTypeDNSLatencies || metricType == constants.MetricTypeCountDNS,
skipEmptyDNSRCode: aggregate == "dnsRCode",
skipEmptyRTT: metricType == constants.MetricTypeFlowRTT,
factor: factor,
},
}, nil
}
Expand Down Expand Up @@ -139,7 +148,7 @@ func (q *TopologyQueryBuilder) Build() string {
// <function>(
// {<label filters>}|<line filters>|json|<json filters>
// |unwrap Bytes|__error__=""[<interval>]
// )
// ) <factor>
// )
// )
// &<query params>&step=<step>
Expand Down Expand Up @@ -172,6 +181,10 @@ func (q *TopologyQueryBuilder) Build() string {
q.appendDNSFilter(sb)
}

if q.topology.skipEmptyRTT {
q.appendRTTFilter(sb)
}

q.appendJSON(sb, true)
if len(q.topology.dataField) > 0 {
sb.WriteString("|unwrap ")
Expand All @@ -184,7 +197,11 @@ func (q *TopologyQueryBuilder) Build() string {
} else {
sb.WriteString(q.topology.rateInterval)
}
sb.WriteString("])))")
sb.WriteString("])")
if len(q.topology.factor) > 0 {
sb.WriteString(q.topology.factor)
}
sb.WriteString("))")
q.appendQueryParams(sb)
sb.WriteString("&step=")
sb.WriteString(q.topology.step)
Expand Down
2 changes: 2 additions & 0 deletions pkg/model/fields/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ const (
DNSID = "DnsId"
DNSLatency = "DnsLatencyMs"
Duplicate = "Duplicate"
TimeFlowRTT = "TimeFlowRttNs"
)

func IsNumeric(v string) bool {
switch v {
case
DNSID,
DNSLatency,
TimeFlowRTT,
Port,
SrcPort,
DstPort,
Expand Down
1 change: 1 addition & 0 deletions pkg/utils/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const (
MetricTypePackets MetricType = "packets"
MetricTypeCount MetricType = "count"
MetricTypeCountDNS MetricType = "countDns"
MetricTypeFlowRTT MetricType = "flowRtt"
MetricTypeDNSLatencies MetricType = "dnsLatencies"
MetricTypeDroppedBytes MetricType = "droppedBytes"
MetricTypeDroppedPackets MetricType = "droppedPackets"
Expand Down
21 changes: 17 additions & 4 deletions web/locales/en/plugin__netobserv-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
"Grid": "Grid",
"ColaGroups": "ColaGroups",
"Invalid": "Invalid",
"rate": "rate",
"Total": "Total",
"Latest rate": "Latest rate",
"Max rate": "Max rate",
"Average rate": "Average rate",
"Latest": "Latest",
"Max": "Max",
"Average": "Average",
"Only average is available for RTT": "Only average is available for RTT",
"Packets": "Packets",
"Bytes": "Bytes",
"RTT": "RTT",
"Type of measurement to show in graphs.": "Type of measurement to show in graphs.",
"Metric type": "Metric type",
"The level of details represented.": "The level of details represented.",
Expand Down Expand Up @@ -207,6 +210,7 @@
"Unable to get overview": "Unable to get overview",
"Clear or reset filters and try again.": "Clear or reset filters and try again.",
"Average latency": "Average latency",
"Average RTT": "Average RTT",
"Total flow count": "Total flow count",
"Show total traffic for the selected filters": "Show total traffic for the selected filters",
"Show total": "Show total",
Expand Down Expand Up @@ -247,12 +251,15 @@
"IP": "IP",
"No information available for this content. Change scope to get more details.": "No information available for this content. Change scope to get more details.",
"Stats": "Stats",
"Flow RTT": "Flow RTT",
"Top 5 rates": "Top 5 rates",
"A -> B": "A -> B",
"In": "In",
"B -> A": "B -> A",
"Out": "Out",
"Both": "Both",
"Average rate": "Average rate",
"Latest rate": "Latest rate",
"Edge": "Edge",
"Unable to get topology": "Unable to get topology",
"Query is slow": "Query is slow",
Expand Down Expand Up @@ -297,6 +304,9 @@
"Filtered byte rate": "Filtered byte rate",
"Filtered sum of top-k packets / filtered total packets": "Filtered sum of top-k packets / filtered total packets",
"packets": "packets",
"Filtered avg RTT / filtered total avg RTT": "Filtered avg RTT / filtered total avg RTT",
"Filtered avg RTT": "Filtered avg RTT",
"ms": "ms",
"Configuration": "Configuration",
"Sampling": "Sampling",
"Cardinality": "Cardinality",
Expand Down Expand Up @@ -361,6 +371,7 @@
"The total aggregated number of bytes.": "The total aggregated number of bytes.",
"The total aggregated number of packets.": "The total aggregated number of packets.",
"Time elapsed between Start Time and End Time.": "Time elapsed between Start Time and End Time.",
"Flow Round Trip Time": "Flow Round Trip Time",
"Collection Time": "Collection Time",
"Reception time of the record by the collector.": "Reception time of the record by the collector.",
"Collection Latency": "Collection Latency",
Expand Down Expand Up @@ -455,8 +466,8 @@
"Specify a single DNS RCODE name like:": "Specify a single DNS RCODE name like:",
"A IANA RCODE number like 0, 3, 9": "A IANA RCODE number like 0, 3, 9",
"A IANA RCODE name like NoError, NXDomain, NotAuth": "A IANA RCODE name like NoError, NXDomain, NotAuth",
"Specify a Flow Round Trip Time in nanoseconds.": "Specify a Flow Round Trip Time in nanoseconds.",
"P": "P",
"Ms": "Ms",
"Pps": "Pps",
"Network overview": "Network overview",
"Top {{limit}} {{type}} rates stacked": "Top {{limit}} {{type}} rates stacked",
Expand Down Expand Up @@ -484,6 +495,8 @@
"The top dropped rates (dropped by the kernel) as bar compared to total as line over the selected interval": "The top dropped rates (dropped by the kernel) as bar compared to total as line over the selected interval",
"Top {{limit}} average DNS latencies": "Top {{limit}} average DNS latencies",
"The average DNS latencies over the selected interval": "The average DNS latencies over the selected interval",
"Top {{limit}} average flow RTT": "Top {{limit}} average flow RTT",
"The average flow Round Trip Time over the selected interval": "The average flow Round Trip Time over the selected interval",
"Top {{limit}} DNS response code": "Top {{limit}} DNS response code",
"The top DNS response code extracted from DNS response headers over the selected interval": "The top DNS response code extracted from DNS response headers over the selected interval",
"Top {{limit}} DNS response code stacked with total": "Top {{limit}} DNS response code stacked with total",
Expand Down
2 changes: 2 additions & 0 deletions web/src/api/ipfix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ export interface Fields {
TimeFlowEndMs: number;
/** Timestamp when this flow was received and processed by the flow collector, in seconds */
TimeReceived: number;
/** Flow Round Trip Time (RTT) in nanoseconds */
TimeFlowRttNs?: number;
/** In conversation tracking, the conversation identifier */
_HashId?: string;
/** In conversation tracking, a flag identifying the first flow */
Expand Down
1 change: 1 addition & 0 deletions web/src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const getTopologyMetrics = (params: FlowQuery, range: number | TimeRange)
return parseTopologyMetrics(
res.result as RawTopologyMetrics[],
range,
params.type,
params.aggregateBy as FlowScope,
res.unixTimestamp,
res.isMock
Expand Down
2 changes: 2 additions & 0 deletions web/src/components/__tests-data__/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,15 @@ export const metric4: RawTopologyMetrics = {
export const metrics = parseTopologyMetrics(
[metric1, metric2, metric3],
{ from: 1653989800, to: 1653990100 },
'bytes',
'resource',
0
) as TopologyMetrics[];

export const droppedMetrics = parseTopologyMetrics(
[metric4],
{ from: 1653989800, to: 1653990100 },
'bytes',
'resource',
0
) as TopologyMetrics[];
35 changes: 22 additions & 13 deletions web/src/components/dropdowns/metric-function-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
import { Dropdown, DropdownItem, DropdownPosition, DropdownToggle } from '@patternfly/react-core';
import { Dropdown, DropdownItem, DropdownPosition, DropdownToggle, Tooltip } from '@patternfly/react-core';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { MetricFunction } from '../../model/flow-query';
import { MetricFunction, MetricType } from '../../model/flow-query';

const metricFunctionOptions: MetricFunction[] = ['last', 'avg', 'max', 'sum'];

export const MetricFunctionDropdown: React.FC<{
selected?: string;
setMetricFunction: (v: MetricFunction) => void;
metricType?: MetricType;
id?: string;
}> = ({ selected, setMetricFunction, id }) => {
}> = ({ selected, setMetricFunction, metricType, id }) => {
const { t } = useTranslation('plugin__netobserv-plugin');
const [metricDropdownOpen, setMetricDropdownOpen] = React.useState(false);

const getMetricDisplay = React.useCallback(
(mf: MetricFunction): string => {
const suffix = metricType !== 'flowRtt' ? ' ' + t('rate') : '';
switch (mf) {
case 'sum':
return t('Total');
case 'last':
return t('Latest rate');
return `${t('Latest')}${suffix}`;
case 'max':
return t('Max rate');
return `${t('Max')}${suffix}`;
case 'avg':
return t('Average rate');
return `${t('Average')}${suffix}`;
}
},
[t]
[metricType, t]
);

return (
Expand All @@ -35,13 +37,20 @@ export const MetricFunctionDropdown: React.FC<{
id={id}
position={DropdownPosition.right}
toggle={
<DropdownToggle
data-test={`${id}-dropdown`}
id={`${id}-dropdown`}
onToggle={() => setMetricDropdownOpen(!metricDropdownOpen)}
<Tooltip
trigger={metricType === 'flowRtt' ? 'mouseenter focus' : ''}
position="top"
content={t('Only average is available for RTT')}
>
{getMetricDisplay(selected as MetricFunction)}
</DropdownToggle>
<DropdownToggle
data-test={`${id}-dropdown`}
id={`${id}-dropdown`}
isDisabled={metricType === 'flowRtt'}
onToggle={() => setMetricDropdownOpen(!metricDropdownOpen)}
>
{getMetricDisplay(selected as MetricFunction)}
</DropdownToggle>
</Tooltip>
}
isOpen={metricDropdownOpen}
dropdownItems={metricFunctionOptions.map(v => (
Expand Down
35 changes: 20 additions & 15 deletions web/src/components/dropdowns/metric-type-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { MetricType } from '../../model/flow-query';

const metricTypeOptions: MetricType[] = ['bytes', 'packets'];
const metricTypeOptions: MetricType[] = ['bytes', 'packets', 'flowRtt'];

export const MetricTypeDropdown: React.FC<{
selected?: string;
setMetricType: (v: MetricType) => void;
isTopology?: boolean;
id?: string;
}> = ({ selected, setMetricType, id }) => {
}> = ({ selected, setMetricType, id, isTopology }) => {
const { t } = useTranslation('plugin__netobserv-plugin');
const [metricDropdownOpen, setMetricDropdownOpen] = React.useState(false);

Expand All @@ -20,6 +21,8 @@ export const MetricTypeDropdown: React.FC<{
return t('Packets');
case 'bytes':
return t('Bytes');
case 'flowRtt':
return t('RTT');
default:
throw new Error('getMetricDisplay called with invalid metricType: ' + metricType);
}
Expand All @@ -42,19 +45,21 @@ export const MetricTypeDropdown: React.FC<{
</DropdownToggle>
}
isOpen={metricDropdownOpen}
dropdownItems={metricTypeOptions.map(v => (
<DropdownItem
data-test={v}
id={v}
key={v}
onClick={() => {
setMetricDropdownOpen(false);
setMetricType(v);
}}
>
{getMetricDisplay(v)}
</DropdownItem>
))}
dropdownItems={metricTypeOptions
.filter(v => isTopology || v !== 'flowRtt')
.map(v => (
<DropdownItem
data-test={v}
id={v}
key={v}
onClick={() => {
setMetricDropdownOpen(false);
setMetricType(v);
}}
>
{getMetricDisplay(v)}
</DropdownItem>
))}
/>
);
};
Expand Down
Loading
Loading