From ba8726e4abe72e199b3b7f55d85f1ecb99a9ea93 Mon Sep 17 00:00:00 2001 From: Joel Takvorian Date: Fri, 19 Apr 2024 15:56:05 +0200 Subject: [PATCH] NETOBSERV-1610: document cardinality warning per field (#623) * NETOBSERV-1610: document cardinality warning per field * Update docs/flows-format.adoc Co-authored-by: Sara Thomas --------- Co-authored-by: Sara Thomas --- controllers/consoleplugin/config/config.go | 19 +++-- .../config/static-frontend-config.yaml | 54 ++++++++++++ .../consoleplugin/consoleplugin_test.go | 23 +++++ docs/flows-format.adoc | 85 ++++++++++++++++--- hack/asciidoc-flows-gen.sh | 6 +- hack/flows-format-header.adoc | 2 + 6 files changed, 172 insertions(+), 17 deletions(-) diff --git a/controllers/consoleplugin/config/config.go b/controllers/consoleplugin/config/config.go index 55cb00f52..d15a06d96 100644 --- a/controllers/consoleplugin/config/config.go +++ b/controllers/consoleplugin/config/config.go @@ -64,12 +64,21 @@ type FilterConfig struct { Placeholder string `yaml:"placeholder,omitempty" json:"placeholder,omitempty"` } +type CardinalityWarn string + +const ( + CardinalityWarnAvoid CardinalityWarn = "avoid" + CardinalityWarnCareful CardinalityWarn = "careful" + CardinalityWarnFine CardinalityWarn = "fine" +) + type FieldConfig struct { - Name string `yaml:"name" json:"name"` - Type string `yaml:"type" json:"type"` - Description string `yaml:"description" json:"description"` - LokiLabel bool `yaml:"lokiLabel,omitempty" json:"lokiLabel,omitempty"` - Filter string `yaml:"filter,omitempty" json:"filter,omitempty"` + Name string `yaml:"name" json:"name"` + Type string `yaml:"type" json:"type"` + Description string `yaml:"description" json:"description"` + LokiLabel bool `yaml:"lokiLabel,omitempty" json:"lokiLabel,omitempty"` + Filter string `yaml:"filter,omitempty" json:"filter,omitempty"` + CardinalityWarn CardinalityWarn `yaml:"cardinalityWarn,omitempty" json:"cardinalityWarn,omitempty"` } type Deduper struct { diff --git a/controllers/consoleplugin/config/static-frontend-config.yaml b/controllers/consoleplugin/config/static-frontend-config.yaml index 07c6ebd8a..a7d60555a 100644 --- a/controllers/consoleplugin/config/static-frontend-config.yaml +++ b/controllers/consoleplugin/config/static-frontend-config.yaml @@ -862,115 +862,149 @@ filters: hint: Specify a TCP handshake Round Trip Time in nanoseconds. # Fields definition, used to generate documentation +# The "cardinalityWarn" property relates to how the field is suitable for usage as a metric label wrt cardinality; it may have 3 values: fine, careful, avoid fields: - name: TimeFlowStartMs type: number description: Start timestamp of this flow, in milliseconds + cardinalityWarn: avoid - name: TimeFlowEndMs type: number description: End timestamp of this flow, in milliseconds + cardinalityWarn: avoid - name: TimeReceived type: number description: Timestamp when this flow was received and processed by the flow collector, in seconds + cardinalityWarn: avoid - name: SrcK8S_Name type: string description: Name of the source Kubernetes object, such as Pod name, Service name or Node name. + cardinalityWarn: careful - name: SrcK8S_Type type: string description: Kind of the source Kubernetes object, such as Pod, Service or Node. lokiLabel: true + cardinalityWarn: fine - name: SrcK8S_OwnerName type: string description: Name of the source owner, such as Deployment name, StatefulSet name, etc. lokiLabel: true + cardinalityWarn: fine - name: SrcK8S_OwnerType type: string description: Kind of the source owner, such as Deployment, StatefulSet, etc. + cardinalityWarn: fine - name: SrcK8S_Namespace type: string description: Source namespace lokiLabel: true + cardinalityWarn: fine - name: SrcAddr type: string description: Source IP address (ipv4 or ipv6) + cardinalityWarn: avoid - name: SrcPort type: number description: Source port + cardinalityWarn: careful - name: SrcMac type: string description: Source MAC address + cardinalityWarn: avoid - name: SrcK8S_HostIP type: string description: Source node IP + cardinalityWarn: fine - name: SrcK8S_HostName type: string description: Source node name + cardinalityWarn: fine - name: SrcK8S_Zone type: string description: Source availability zone lokiLabel: true + cardinalityWarn: fine - name: SrcSubnetLabel type: string description: Source subnet label + cardinalityWarn: fine - name: DstK8S_Name type: string description: Name of the destination Kubernetes object, such as Pod name, Service name or Node name. + cardinalityWarn: careful - name: DstK8S_Type type: string description: Kind of the destination Kubernetes object, such as Pod, Service or Node. lokiLabel: true + cardinalityWarn: fine - name: DstK8S_OwnerName type: string description: Name of the destination owner, such as Deployment name, StatefulSet name, etc. lokiLabel: true + cardinalityWarn: fine - name: DstK8S_OwnerType type: string description: Kind of the destination owner, such as Deployment, StatefulSet, etc. + cardinalityWarn: fine - name: DstK8S_Namespace type: string description: Destination namespace lokiLabel: true + cardinalityWarn: fine - name: DstAddr type: string description: Destination IP address (ipv4 or ipv6) + cardinalityWarn: avoid - name: DstPort type: number description: Destination port + cardinalityWarn: careful - name: DstMac type: string description: Destination MAC address + cardinalityWarn: avoid - name: DstK8S_HostIP type: string description: Destination node IP + cardinalityWarn: fine - name: DstK8S_HostName type: string description: Destination node name + cardinalityWarn: fine - name: DstK8S_Zone type: string description: Destination availability zone lokiLabel: true + cardinalityWarn: fine - name: DstSubnetLabel type: string description: Destination subnet label + cardinalityWarn: fine - name: K8S_FlowLayer type: string description: "Flow layer: 'app' or 'infra'" + cardinalityWarn: fine - name: Proto type: number description: L4 protocol + cardinalityWarn: fine - name: Dscp type: number description: Differentiated Services Code Point (DSCP) value + cardinalityWarn: fine - name: IcmpType type: number description: ICMP type + cardinalityWarn: fine - name: IcmpCode type: number description: ICMP code + cardinalityWarn: fine - name: Duplicate type: boolean description: Indicates if this flow was also captured from another interface on the same host lokiLabel: true + cardinalityWarn: fine - name: FlowDirection type: number description: | @@ -979,15 +1013,18 @@ fields: - 1: Egress (outgoing traffic, from the node observation point) + - 2: Inner (with the same source and destination node) lokiLabel: true + cardinalityWarn: fine - name: IfDirections type: number description: | Flow directions from the network interface observation point. Can be one of: + - 0: Ingress (interface incoming traffic) + - 1: Egress (interface outgoing traffic) + cardinalityWarn: fine - name: Interfaces type: string description: Network interfaces + cardinalityWarn: careful - name: Flags type: number description: | @@ -995,55 +1032,72 @@ fields: - SYN+ACK (0x100) + - FIN+ACK (0x200) + - RST+ACK (0x400) + cardinalityWarn: fine - name: Bytes type: number description: Number of bytes + cardinalityWarn: avoid - name: Packets type: number description: Number of packets + cardinalityWarn: avoid - name: PktDropBytes type: number description: Number of bytes dropped by the kernel + cardinalityWarn: avoid - name: PktDropPackets type: number description: Number of packets dropped by the kernel + cardinalityWarn: avoid - name: PktDropLatestState type: string description: TCP state on last dropped packet filter: pkt_drop_state # couldn't guess from config + cardinalityWarn: fine - name: PktDropLatestDropCause type: string description: Latest drop cause filter: pkt_drop_cause # couldn't guess from config + cardinalityWarn: fine - name: PktDropLatestFlags type: number description: TCP flags on last dropped packet + cardinalityWarn: fine - name: DnsId type: number description: DNS record id + cardinalityWarn: avoid - name: DnsLatencyMs type: number description: Time between a DNS request and response, in milliseconds + cardinalityWarn: avoid - name: DnsFlags type: number description: DNS flags for DNS record + cardinalityWarn: fine - name: DnsFlagsResponseCode type: string description: Parsed DNS header RCODEs name + cardinalityWarn: fine - name: DnsErrno type: number description: Error number returned from DNS tracker ebpf hook function + cardinalityWarn: fine - name: TimeFlowRttNs type: number description: TCP Smoothed Round Trip Time (SRTT), in nanoseconds + cardinalityWarn: avoid - name: K8S_ClusterName type: string description: Cluster name or identifier lokiLabel: true + cardinalityWarn: fine - name: _RecordType type: string description: "Type of record: 'flowLog' for regular flow logs, or 'newConnection', 'heartbeat', 'endConnection' for conversation tracking" lokiLabel: true + cardinalityWarn: fine - name: _HashId type: string description: In conversation tracking, the conversation identifier + cardinalityWarn: avoid diff --git a/controllers/consoleplugin/consoleplugin_test.go b/controllers/consoleplugin/consoleplugin_test.go index cd06fd4af..461a2e9d7 100644 --- a/controllers/consoleplugin/consoleplugin_test.go +++ b/controllers/consoleplugin/consoleplugin_test.go @@ -2,6 +2,7 @@ package consoleplugin import ( "encoding/json" + "strings" "testing" promConfig "github.com/prometheus/common/config" @@ -534,3 +535,25 @@ func TestNoMissingFields(t *testing.T) { } assert.Empty(t, missing, "Missing fields should be added in static config file, under 'fields'") } + +func TestFieldsCardinalityWarns(t *testing.T) { + var cfg config.FrontendConfig + err := yaml.Unmarshal(staticFrontendConfig, &cfg) + assert.NoError(t, err) + + allowed := []config.CardinalityWarn{config.CardinalityWarnAvoid, config.CardinalityWarnCareful, config.CardinalityWarnFine} + mapCardinality := map[string]config.CardinalityWarn{} + for _, field := range cfg.Fields { + assert.Containsf(t, allowed, field.CardinalityWarn, "Field %s: cardinalityWarn '%s' is invalid", field.Name, field.CardinalityWarn) + mapCardinality[field.Name] = field.CardinalityWarn + } + + for name, card := range mapCardinality { + if strings.HasPrefix(name, "Src") { + base := strings.TrimPrefix(name, "Src") + dst, ok := mapCardinality["Dst"+base] + assert.True(t, ok) + assert.Equalf(t, card, dst, "Cardinality for %s and %s differs", name, "Dst"+base) + } + } +} diff --git a/docs/flows-format.adoc b/docs/flows-format.adoc index 9311b2b83..ed2a5c63d 100644 --- a/docs/flows-format.adoc +++ b/docs/flows-format.adoc @@ -9,105 +9,132 @@ The "Filter ID" column shows which related name to use when defining Quick Filte The "Loki label" column is useful when querying Loki directly: label fields need to be selected using link:https://grafana.com/docs/loki/latest/logql/log_queries/#log-stream-selector[stream selectors]. +The "Cardinality" column gives information about the implied metric cardinality if this field is used as a Prometheus label with the `FlowMetrics` API. Refer to the `FlowMetrics` documentation for more information on using this API. -[cols="1,1,3,1,1",options="header"] + +[cols="1,1,3,1,1,1",options="header"] |=== -| Name | Type | Description | Filter ID | Loki label +| Name | Type | Description | Filter ID | Loki label | Cardinality | `Bytes` | number | Number of bytes | n/a | no +| avoid | `DnsErrno` | number | Error number returned from DNS tracker ebpf hook function | `dns_errno` | no +| fine | `DnsFlags` | number | DNS flags for DNS record | n/a | no +| fine | `DnsFlagsResponseCode` | string | Parsed DNS header RCODEs name | `dns_flag_response_code` | no +| fine | `DnsId` | number | DNS record id | `dns_id` | no +| avoid | `DnsLatencyMs` | number | Time between a DNS request and response, in milliseconds | `dns_latency` | no +| avoid | `Dscp` | number | Differentiated Services Code Point (DSCP) value | `dscp` | no +| fine | `DstAddr` | string | Destination IP address (ipv4 or ipv6) | `dst_address` | no +| avoid | `DstK8S_HostIP` | string | Destination node IP | `dst_host_address` | no +| fine | `DstK8S_HostName` | string | Destination node name | `dst_host_name` | no +| fine | `DstK8S_Name` | string | Name of the destination Kubernetes object, such as Pod name, Service name or Node name. | `dst_name` | no +| careful | `DstK8S_Namespace` | string | Destination namespace | `dst_namespace` | yes +| fine | `DstK8S_OwnerName` | string | Name of the destination owner, such as Deployment name, StatefulSet name, etc. | `dst_owner_name` | yes +| fine | `DstK8S_OwnerType` | string | Kind of the destination owner, such as Deployment, StatefulSet, etc. | `dst_kind` | no +| fine | `DstK8S_Type` | string | Kind of the destination Kubernetes object, such as Pod, Service or Node. | `dst_kind` | yes +| fine | `DstK8S_Zone` | string | Destination availability zone | `dst_zone` | yes +| fine | `DstMac` | string | Destination MAC address | `dst_mac` | no +| avoid | `DstPort` | number | Destination port | `dst_port` | no +| careful +| `DstSubnetLabel` +| string +| Destination subnet label +| `dst_subnet_label` +| no +| fine | `Duplicate` | boolean | Indicates if this flow was also captured from another interface on the same host | n/a | yes +| fine | `Flags` | number | Logical OR combination of unique TCP flags comprised in the flow, as per RFC-9293, with additional custom flags to represent the following per-packet combinations: + @@ -116,164 +143,202 @@ The "Loki label" column is useful when querying Loki directly: label fields need - RST+ACK (0x400) | n/a | no +| fine | `FlowDirection` | number -| Flow direction from the node observation point. Can be one of: + +| Flow interpreted direction from the node observation point. Can be one of: + - 0: Ingress (incoming traffic, from the node observation point) + - 1: Egress (outgoing traffic, from the node observation point) + - 2: Inner (with the same source and destination node) -| `direction` +| `node_direction` | yes +| fine | `IcmpCode` | number | ICMP code | `icmp_code` | no +| fine | `IcmpType` | number | ICMP type | `icmp_type` | no -| `IfDirection` +| fine +| `IfDirections` | number -| Flow direction from the network interface observation point. Can be one of: + +| Flow directions from the network interface observation point. Can be one of: + - 0: Ingress (interface incoming traffic) + - 1: Egress (interface outgoing traffic) -| n/a +| `ifdirections` | no -| `Interface` +| fine +| `Interfaces` | string -| Network interface -| `interface` +| Network interfaces +| `interfaces` | no +| careful | `K8S_ClusterName` | string | Cluster name or identifier | `cluster_name` | yes +| fine | `K8S_FlowLayer` | string | Flow layer: 'app' or 'infra' | `flow_layer` | no +| fine | `Packets` | number | Number of packets | n/a | no +| avoid | `PktDropBytes` | number | Number of bytes dropped by the kernel | n/a | no +| avoid | `PktDropLatestDropCause` | string | Latest drop cause | `pkt_drop_cause` | no +| fine | `PktDropLatestFlags` | number | TCP flags on last dropped packet | n/a | no +| fine | `PktDropLatestState` | string | TCP state on last dropped packet | `pkt_drop_state` | no +| fine | `PktDropPackets` | number | Number of packets dropped by the kernel | n/a | no +| avoid | `Proto` | number | L4 protocol | `protocol` | no +| fine | `SrcAddr` | string | Source IP address (ipv4 or ipv6) | `src_address` | no +| avoid | `SrcK8S_HostIP` | string | Source node IP | `src_host_address` | no +| fine | `SrcK8S_HostName` | string | Source node name | `src_host_name` | no +| fine | `SrcK8S_Name` | string | Name of the source Kubernetes object, such as Pod name, Service name or Node name. | `src_name` | no +| careful | `SrcK8S_Namespace` | string | Source namespace | `src_namespace` | yes +| fine | `SrcK8S_OwnerName` | string | Name of the source owner, such as Deployment name, StatefulSet name, etc. | `src_owner_name` | yes +| fine | `SrcK8S_OwnerType` | string | Kind of the source owner, such as Deployment, StatefulSet, etc. | `src_kind` | no +| fine | `SrcK8S_Type` | string | Kind of the source Kubernetes object, such as Pod, Service or Node. | `src_kind` | yes +| fine | `SrcK8S_Zone` | string | Source availability zone | `src_zone` | yes +| fine | `SrcMac` | string | Source MAC address | `src_mac` | no +| avoid | `SrcPort` | number | Source port | `src_port` | no +| careful +| `SrcSubnetLabel` +| string +| Source subnet label +| `src_subnet_label` +| no +| fine | `TimeFlowEndMs` | number | End timestamp of this flow, in milliseconds | n/a | no +| avoid | `TimeFlowRttNs` | number | TCP Smoothed Round Trip Time (SRTT), in nanoseconds | `time_flow_rtt` | no +| avoid | `TimeFlowStartMs` | number | Start timestamp of this flow, in milliseconds | n/a | no +| avoid | `TimeReceived` | number | Timestamp when this flow was received and processed by the flow collector, in seconds | n/a | no +| avoid | `_HashId` | string | In conversation tracking, the conversation identifier | `id` | no +| avoid | `_RecordType` | string | Type of record: 'flowLog' for regular flow logs, or 'newConnection', 'heartbeat', 'endConnection' for conversation tracking | `type` | yes +| fine |=== diff --git a/hack/asciidoc-flows-gen.sh b/hack/asciidoc-flows-gen.sh index d144d47b2..8c464a0d7 100755 --- a/hack/asciidoc-flows-gen.sh +++ b/hack/asciidoc-flows-gen.sh @@ -8,9 +8,9 @@ echo "// Automatically generated by '$0'. Do not edit, or make the NETOBSERV tea cat ./hack/flows-format-header.adoc >> $ADOC echo -e "\n" >> $ADOC -echo -e '[cols="1,1,3,1,1",options="header"]' >> $ADOC +echo -e '[cols="1,1,3,1,1,1",options="header"]' >> $ADOC echo -e '|===' >> $ADOC -echo -e '| Name | Type | Description | Filter ID | Loki label' >> $ADOC +echo -e '| Name | Type | Description | Filter ID | Loki label | Cardinality' >> $ADOC nbfields=$(yq '.fields | length' $SOURCE) @@ -36,11 +36,13 @@ for i in $(seq 0 $(( $nbfields-1 )) ); do else isLabel="no" fi + cardWarn=$(printf "$entry" | yq ".cardinalityWarn") echo -e "| \`$name\`" >> $ADOC echo -e "| $type" >> $ADOC echo -e "| $desc" >> $ADOC echo -e "| $filter" >> $ADOC echo -e "| $isLabel" >> $ADOC + echo -e "| $cardWarn" >> $ADOC done echo -e '|===' >> $ADOC diff --git a/hack/flows-format-header.adoc b/hack/flows-format-header.adoc index 371c6eb4f..7dfb391f0 100644 --- a/hack/flows-format-header.adoc +++ b/hack/flows-format-header.adoc @@ -7,3 +7,5 @@ This is the specification of the network flows format. That format is used when The "Filter ID" column shows which related name to use when defining Quick Filters (see `spec.consolePlugin.quickFilters` in the `FlowCollector` specification). The "Loki label" column is useful when querying Loki directly: label fields need to be selected using link:https://grafana.com/docs/loki/latest/logql/log_queries/#log-stream-selector[stream selectors]. + +The "Cardinality" column gives information about the implied metric cardinality if this field was to be used as a Prometheus label with the `FlowMetrics` API. Refer to the `FlowMetrics` documentation for more information on using this API.