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

Dependency Plugin Additional Options #200

Merged
merged 1 commit into from
May 3, 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
2 changes: 1 addition & 1 deletion build/charts/theia/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Kubernetes: `>= 1.16.0-0`
| grafana.enable | bool | `true` | Determine whether to install Grafana. It is used as a data visualization and monitoring tool. |
| grafana.homeDashboard | string | `"homepage.json"` | Default home dashboard. |
| grafana.image | object | `{"pullPolicy":"IfNotPresent","repository":"projects.registry.vmware.com/antrea/theia-grafana","tag":"8.3.3"}` | Container image used by Grafana. |
| grafana.installPlugins | list | `["https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-sankey-plugin-1.0.2.zip;theia-grafana-sankey-plugin","https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-chord-plugin-1.0.1.zip;theia-grafana-chord-plugin","https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-dependency-plugin-1.0.1.zip;theia-grafana-dependency-plugin","grafana-clickhouse-datasource 1.0.1"]` | Grafana plugins to install. |
| grafana.installPlugins | list | `["https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-sankey-plugin-1.0.2.zip;theia-grafana-sankey-plugin","https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-chord-plugin-1.0.1.zip;theia-grafana-chord-plugin","https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-dependency-plugin-1.0.2.zip;theia-grafana-dependency-plugin","grafana-clickhouse-datasource 1.0.1"]` | Grafana plugins to install. |
| grafana.log | object | `{"daily_rotate":"true","level":"info","log_rotate":"true","max_days":"7","max_lines":"1000000","max_size_shift":"27","mode":"console file"}` | Grafana logging options. |
| grafana.log.daily_rotate | string | `"true"` | Enable daily rotation of files, valid options are false or true. Default is true. Only applicable when “file” used in [log] mode. |
| grafana.log.level | string | `"info"` | Logging level. Options are “debug”, “info”, “warn”, “error”, and “critical”. Default is info. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 4,
"iteration": 1677633025540,
"id": 8,
"iteration": 1682533463233,
"links": [],
"liveNow": false,
"panels": [
Expand All @@ -39,6 +39,8 @@
},
"id": 2,
"options": {
"color": "yellow",
"groupByLabel": false,
"seriesCountSize": "sm",
"showSeriesCount": false,
"text": "Default value of text input option"
Expand All @@ -58,7 +60,7 @@
}
},
"queryType": "sql",
"rawSql": "SELECT sourcePodName, sourcePodNamespace, sourceNodeName, destinationPodName, destinationNodeName, destinationServicePortName, octetDeltaCount FROM flows\nWHERE sourcePodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodName != ''\nAND sourcePodName != ''\nAND octetDeltaCount != 0\nAND $__timeFilter(flowEndSeconds)\nORDER BY flowEndSeconds DESC",
"rawSql": "SELECT sourcePodName, sourcePodLabels, sourcePodNamespace, sourceNodeName, destinationPodName, destinationPodLabels, destinationNodeName, destinationServicePortName, octetDeltaCount FROM flows\nWHERE sourcePodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodName != ''\nAND sourcePodName != ''\nAND octetDeltaCount != 0\nAND $__timeFilter(flowEndSeconds)\nORDER BY flowEndSeconds DESC",
"refId": "A"
}
],
Expand Down Expand Up @@ -100,6 +102,6 @@
"timezone": "",
"title": "network_topology_dashboard",
"uid": "yRVDEad4k",
"version": 1,
"version": 2,
"weekStart": ""
}
2 changes: 1 addition & 1 deletion build/charts/theia/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ grafana:
installPlugins:
- https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-sankey-plugin-1.0.2.zip;theia-grafana-sankey-plugin
- https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-chord-plugin-1.0.1.zip;theia-grafana-chord-plugin
- https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-dependency-plugin-1.0.1.zip;theia-grafana-dependency-plugin
- https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-dependency-plugin-1.0.2.zip;theia-grafana-dependency-plugin
- grafana-clickhouse-datasource 1.0.1
# -- The dashboards to be displayed in Grafana UI. The files must be put under
# provisioning/dashboards.
Expand Down
12 changes: 7 additions & 5 deletions build/yamls/flow-visibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3083,8 +3083,8 @@ data:
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 4,
"iteration": 1677633025540,
"id": 8,
"iteration": 1682533463233,
"links": [],
"liveNow": false,
"panels": [
Expand All @@ -3101,6 +3101,8 @@ data:
},
"id": 2,
"options": {
"color": "yellow",
"groupByLabel": false,
"seriesCountSize": "sm",
"showSeriesCount": false,
"text": "Default value of text input option"
Expand All @@ -3120,7 +3122,7 @@ data:
}
},
"queryType": "sql",
"rawSql": "SELECT sourcePodName, sourcePodNamespace, sourceNodeName, destinationPodName, destinationNodeName, destinationServicePortName, octetDeltaCount FROM flows\nWHERE sourcePodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodName != ''\nAND sourcePodName != ''\nAND octetDeltaCount != 0\nAND $__timeFilter(flowEndSeconds)\nORDER BY flowEndSeconds DESC",
"rawSql": "SELECT sourcePodName, sourcePodLabels, sourcePodNamespace, sourceNodeName, destinationPodName, destinationPodLabels, destinationNodeName, destinationServicePortName, octetDeltaCount FROM flows\nWHERE sourcePodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodNamespace NOT IN ('kube-system', 'flow-visibility', 'flow-aggregator')\nAND destinationPodName != ''\nAND sourcePodName != ''\nAND octetDeltaCount != 0\nAND $__timeFilter(flowEndSeconds)\nORDER BY flowEndSeconds DESC",
"refId": "A"
}
],
Expand Down Expand Up @@ -3162,7 +3164,7 @@ data:
"timezone": "",
"title": "network_topology_dashboard",
"uid": "yRVDEad4k",
"version": 1,
"version": 2,
"weekStart": ""
}
networkpolicy_dashboard.json: |
Expand Down Expand Up @@ -6366,7 +6368,7 @@ spec:
containers:
- env:
- name: GF_INSTALL_PLUGINS
value: https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-sankey-plugin-1.0.2.zip;theia-grafana-sankey-plugin,https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-chord-plugin-1.0.1.zip;theia-grafana-chord-plugin,https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-dependency-plugin-1.0.1.zip;theia-grafana-dependency-plugin,grafana-clickhouse-datasource
value: https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-sankey-plugin-1.0.2.zip;theia-grafana-sankey-plugin,https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-chord-plugin-1.0.1.zip;theia-grafana-chord-plugin,https://downloads.antrea.io/artifacts/grafana-custom-plugins/theia-grafana-dependency-plugin-1.0.2.zip;theia-grafana-dependency-plugin,grafana-clickhouse-datasource
1.0.1
- name: CLICKHOUSE_USERNAME
valueFrom:
Expand Down
7 changes: 7 additions & 0 deletions docs/network-flow-visibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,13 @@ the selected time range.

<img src="https://downloads.antrea.io/static/04132023/flow-visibility-network-topology-0.png" width="400" alt="Network Topology Dashboard service dependency graph">

By editing the panel, users can group Pods by label, allowing Pod squares in
the diagram to represent a set of Pods with the same label value. It is also
possible to choose the color of the Pod squares to be red, yellow, green, or
blue.

<img src="https://downloads.antrea.io/static/05022023/flow-visibility-network-topology-1.png" width="400" alt="Network Topology Dashboard additional configuration options">

### Dashboard Customization

If you would like to make any change to any of the pre-built dashboards, or build
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.0.2 - 05-02-2023

Added "Group by Pod Label" toggle and Pod square color choices.

## 1.0.1 - 04-13-2023

Added theming for dependency plugin.
Expand Down
24 changes: 19 additions & 5 deletions plugins/grafana-custom-plugins/grafana-dependency-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,22 @@ loading of data for the Service Dependency Graph Plugin, the query is expected
to return the following fields, in arbitrary order.

- field 1: sourcePodName value with name or an alias of `sourcePodName`
- field 2: sourceNodeName value with name or an alias of `sourceNodeName`
- field 3: destinationPodName value with name or an alias of `destinationPodName`
- field 4: destinationNodeName value with name or an alias of `destinationNodeName`
- field 5: destinationServicePortName value with name or an alias of `destinationServicePortName`
- field 6: octetDeltaCount value with name or an alias of `octetDeltaCount`
- field 2: sourcePodLabels value with name or alias of `sourcePodLabels`
- field 3: sourceNodeName value with name or an alias of `sourceNodeName`
- field 4: destinationPodName value with name or an alias of `destinationPodName`
- field 5: destinationPodLabels value with name or an alias of `destinationPodLabels`
- field 6: destinationNodeName value with name or an alias of `destinationNodeName`
- field 7: destinationServicePortName value with name or an alias of `destinationServicePortName`
- field 8: octetDeltaCount value with name or an alias of `octetDeltaCount`

ClickHouse query example:

```sql
SELECT sourcePodName,
sourcePodLabels,
sourceNodeName,
destinationPodName,
destinationPodLabels,
destinationNodeName,
destinationServicePortName,
octetDeltaCount
Expand Down Expand Up @@ -106,6 +110,16 @@ installed panels will appear. For more information, visit the docs on [Grafana p

## Customization

### 3. Customize the Panel Options

Users can customize the panel by editing its options and choosing to group the
diagram based on a chosen Pod label. It is also possible to change the color
of the Pod squares in the diagram.

<img src="https://user-images.githubusercontent.com/10016630/233191376-7cf471b8-5e3e-473a-8696-da25ab981066.png" width="400" alt="Panel Option Editor View">

### 4. Further Customization

This plugin is built with [@grafana/toolkit](https://www.npmjs.com/package/@grafana/toolkit),
which is a CLI that enables efficient development of Grafana plugins. To customize
the plugin and do local testings:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ export const DependencyPanel: React.FC<Props> = ({ options, data, width, height
const theme = useTheme2();
const frame = data.series[0];
const sourcePodNames = frame.fields.find((field) => field.name === 'sourcePodName');
const sourcePodLabels = frame.fields.find((field) => field.name === 'sourcePodLabels');
const sourceNodeNames = frame.fields.find((field) => field.name === 'sourceNodeName');
const destinationPodNames = frame.fields.find((field) => field.name === 'destinationPodName');
const destinationPodLabels = frame.fields.find((field) => field.name === 'destinationPodLabels');
const destinationNodeNames = frame.fields.find((field) => field.name === 'destinationNodeName');
const destinationServicePortNames = frame.fields.find((field) => field.name === 'destinationServicePortName');
const octetDeltaCounts = frame.fields.find((field) => field.name === 'octetDeltaCount');
Expand All @@ -29,12 +31,27 @@ export const DependencyPanel: React.FC<Props> = ({ options, data, width, height
let srcToDestMap = new Map<string, Map<string, number>>();

let graphString = 'graph LR;\n';
let boxColor;
switch(options.color) {
case 'red':
boxColor = theme.colors.error.main;
break;
case 'yellow':
boxColor = theme.colors.warning.main;
break;
case 'green':
boxColor = theme.colors.success.main;
break;
case 'blue':
boxColor = theme.colors.primary.main;
break;
}

mermaid.initialize({
startOnLoad: true,
theme: 'base',
themeVariables: {
primaryColor: theme.colors.warning.main,
primaryColor: boxColor,
secondaryColor: theme.colors.background.canvas,
tertiaryColor: theme.colors.background.canvas,
primaryTextColor: theme.colors.text.maxContrast,
Expand All @@ -44,26 +61,44 @@ export const DependencyPanel: React.FC<Props> = ({ options, data, width, height

for (let i = 0; i < frame.length; i++) {
const sourcePodName = sourcePodNames?.values.get(i);
const sourcePodLabel = sourcePodLabels?.values.get(i);
const sourceNodeName = sourceNodeNames?.values.get(i);
const destinationPodName = destinationPodNames?.values.get(i);
const destinationPodLabel = destinationPodLabels?.values.get(i);
const destinationNodeName = destinationNodeNames?.values.get(i);
const destinationServicePortName = destinationServicePortNames?.values.get(i);
const octetDeltaCount = octetDeltaCounts?.values.get(i);

function getName(groupByLabel: boolean, source: boolean, labelJSON: string) {
if(!groupByLabel || labelJSON === undefined || options.labelName === undefined) {
return source ? sourcePodName : destinationPodName;
}
let labels = JSON.parse(labelJSON);
if(labels[options.labelName] !== undefined) {
return labels[options.labelName];
}
return sourcePodName;
}

let groupByPodLabel = options.groupByPodLabel;
let srcName = getName(groupByPodLabel, true, sourcePodLabel);
let dstName = getName(groupByPodLabel, false, destinationPodLabel);

// determine which nodes contain which pods
if (nodeToPodMap.has(sourceNodeName) && !nodeToPodMap.get(sourceNodeName)?.includes(sourcePodName)) {
nodeToPodMap.get(sourceNodeName)?.push(sourcePodName);
if (nodeToPodMap.has(sourceNodeName) && !nodeToPodMap.get(sourceNodeName)?.includes(srcName)) {
nodeToPodMap.get(sourceNodeName)?.push(srcName);
} else if (!nodeToPodMap.has(sourceNodeName)) {
nodeToPodMap.set(sourceNodeName, [sourcePodName]);
nodeToPodMap.set(sourceNodeName, [srcName]);
}
if (nodeToPodMap.has(destinationNodeName) && !nodeToPodMap.get(destinationNodeName)?.includes(destinationPodName)) {
nodeToPodMap.get(destinationNodeName)?.push(destinationPodName);
if (nodeToPodMap.has(destinationNodeName) && !nodeToPodMap.get(destinationNodeName)?.includes(dstName)) {
nodeToPodMap.get(destinationNodeName)?.push(dstName);
} else if (!nodeToPodMap.has(destinationNodeName)) {
nodeToPodMap.set(destinationNodeName, [destinationPodName]);
nodeToPodMap.set(destinationNodeName, [dstName]);
}

// determine how much traffic is being sent
let pod_src = sourceNodeName+'_pod_'+sourcePodName;
let pod_dst = destinationNodeName+'_pod_'+destinationPodName;
let pod_src = sourceNodeName+'_pod_'+srcName;
let pod_dst = destinationNodeName+'_pod_'+dstName;
let svc_dst = 'svc_'+destinationServicePortName;
let dests = new Map<string, number>();
dests.set(pod_dst, octetDeltaCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,38 @@ import { PanelPlugin } from '@grafana/data';
import { DependencyOptions } from './types';
import { DependencyPanel } from './DependencyPanel';

export const plugin = new PanelPlugin<DependencyOptions>(DependencyPanel).setPanelOptions((builder) => builder);
export const plugin = new PanelPlugin<DependencyOptions>(DependencyPanel).setPanelOptions((builder) => builder.addBooleanSwitch({
path: 'groupByPodLabel',
name: 'Group by Pod Label',
defaultValue: false,
}).addTextInput({
path: 'labelName',
name: 'Label Name',
settings: {
placeholder: 'app',
},
}).addRadio({
path: 'color',
name: 'Box Color',
defaultValue: 'yellow',
settings: {
options: [
{
value: 'red',
label: 'Red',
},
{
value: 'yellow',
label: 'Yellow',
},
{
value: 'green',
label: 'Green',
},
{
value: 'blue',
label: 'Blue',
}
]
},
}));
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export interface DependencyOptions {}
type BoxColor = 'red' | 'yellow' | 'green' | 'blue'

export interface DependencyOptions {
groupByPodLabel: boolean;
labelName: string;
color: BoxColor;
}