diff --git a/build/charts/theia/README.md b/build/charts/theia/README.md
index 870ba3ee2..d0469ddce 100644
--- a/build/charts/theia/README.md
+++ b/build/charts/theia/README.md
@@ -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-chord-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. |
diff --git a/build/charts/theia/provisioning/dashboards/network_topology_dashboard.json b/build/charts/theia/provisioning/dashboards/network_topology_dashboard.json
index b70f420ad..538677994 100644
--- a/build/charts/theia/provisioning/dashboards/network_topology_dashboard.json
+++ b/build/charts/theia/provisioning/dashboards/network_topology_dashboard.json
@@ -21,8 +21,8 @@
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
- "id": 4,
- "iteration": 1677633025540,
+ "id": 8,
+ "iteration": 1682533463233,
"links": [],
"liveNow": false,
"panels": [
@@ -39,6 +39,8 @@
},
"id": 2,
"options": {
+ "color": "yellow",
+ "groupByLabel": false,
"seriesCountSize": "sm",
"showSeriesCount": false,
"text": "Default value of text input option"
@@ -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"
}
],
@@ -100,6 +102,6 @@
"timezone": "",
"title": "network_topology_dashboard",
"uid": "yRVDEad4k",
- "version": 1,
+ "version": 2,
"weekStart": ""
}
diff --git a/build/charts/theia/values.yaml b/build/charts/theia/values.yaml
index d17c0f285..fa5eb98f7 100644
--- a/build/charts/theia/values.yaml
+++ b/build/charts/theia/values.yaml
@@ -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-chord-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.
diff --git a/build/yamls/flow-visibility.yml b/build/yamls/flow-visibility.yml
index 9548107b4..ade8ddf79 100644
--- a/build/yamls/flow-visibility.yml
+++ b/build/yamls/flow-visibility.yml
@@ -3083,8 +3083,8 @@ data:
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
- "id": 4,
- "iteration": 1677633025540,
+ "id": 8,
+ "iteration": 1682533463233,
"links": [],
"liveNow": false,
"panels": [
@@ -3101,6 +3101,8 @@ data:
},
"id": 2,
"options": {
+ "color": "yellow",
+ "groupByLabel": false,
"seriesCountSize": "sm",
"showSeriesCount": false,
"text": "Default value of text input option"
@@ -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"
}
],
@@ -3162,7 +3164,7 @@ data:
"timezone": "",
"title": "network_topology_dashboard",
"uid": "yRVDEad4k",
- "version": 1,
+ "version": 2,
"weekStart": ""
}
networkpolicy_dashboard.json: |
@@ -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-chord-plugin-1.0.2.zip;theia-grafana-dependency-plugin,grafana-clickhouse-datasource
1.0.1
- name: CLICKHOUSE_USERNAME
valueFrom:
diff --git a/docs/network-flow-visibility.md b/docs/network-flow-visibility.md
index b751b4d59..bda012c3d 100644
--- a/docs/network-flow-visibility.md
+++ b/docs/network-flow-visibility.md
@@ -764,6 +764,13 @@ the selected time range.
+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.
+
+
+
### Dashboard Customization
If you would like to make any change to any of the pre-built dashboards, or build
diff --git a/plugins/grafana-custom-plugins/grafana-dependency-plugin/CHANGELOG.md b/plugins/grafana-custom-plugins/grafana-dependency-plugin/CHANGELOG.md
index 987acf081..188494c95 100644
--- a/plugins/grafana-custom-plugins/grafana-dependency-plugin/CHANGELOG.md
+++ b/plugins/grafana-custom-plugins/grafana-dependency-plugin/CHANGELOG.md
@@ -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.
diff --git a/plugins/grafana-custom-plugins/grafana-dependency-plugin/README.md b/plugins/grafana-custom-plugins/grafana-dependency-plugin/README.md
index 0b34736f1..809b3538d 100644
--- a/plugins/grafana-custom-plugins/grafana-dependency-plugin/README.md
+++ b/plugins/grafana-custom-plugins/grafana-dependency-plugin/README.md
@@ -42,18 +42,24 @@ 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: sourcePodNamespace value with name or alias of `sourcePodNamespace`
+- field 4: sourceNodeName value with name or an alias of `sourceNodeName`
+- field 5: destinationPodName value with name or an alias of `destinationPodName`
+- field 6: destinationPodLabels value with name or an alias of `destinationPodLabels`
+- field 7: destinationNodeName value with name or an alias of `destinationNodeName`
+- field 8: destinationServicePortName value with name or an alias of `destinationServicePortName`
+- field 9: octetDeltaCount value with name or an alias of `octetDeltaCount`
ClickHouse query example:
```sql
SELECT sourcePodName,
+sourcePodLabels,
+sourcePodNamespace,
sourceNodeName,
destinationPodName,
+destinationPodLabels,
destinationNodeName,
destinationServicePortName,
octetDeltaCount
@@ -106,6 +112,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.
+
+
+
+### 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:
diff --git a/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/DependencyPanel.tsx b/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/DependencyPanel.tsx
index 3ecf05696..c11434f39 100644
--- a/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/DependencyPanel.tsx
+++ b/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/DependencyPanel.tsx
@@ -19,8 +19,10 @@ export const DependencyPanel: React.FC = ({ 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');
@@ -29,12 +31,27 @@ export const DependencyPanel: React.FC = ({ options, data, width, height
let srcToDestMap = new Map>();
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,
@@ -44,26 +61,44 @@ export const DependencyPanel: React.FC = ({ 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();
dests.set(pod_dst, octetDeltaCount);
diff --git a/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/module.ts b/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/module.ts
index b45186ea5..1f291f7f4 100644
--- a/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/module.ts
+++ b/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/module.ts
@@ -2,4 +2,38 @@ import { PanelPlugin } from '@grafana/data';
import { DependencyOptions } from './types';
import { DependencyPanel } from './DependencyPanel';
-export const plugin = new PanelPlugin(DependencyPanel).setPanelOptions((builder) => builder);
+export const plugin = new PanelPlugin(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',
+ }
+ ]
+ },
+}));
diff --git a/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/types.ts b/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/types.ts
index bfd93b897..613122782 100644
--- a/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/types.ts
+++ b/plugins/grafana-custom-plugins/grafana-dependency-plugin/src/types.ts
@@ -1 +1,7 @@
-export interface DependencyOptions {}
+type BoxColor = 'red' | 'yellow' | 'green' | 'blue'
+
+export interface DependencyOptions {
+ groupByPodLabel: boolean;
+ labelName: string;
+ color: BoxColor;
+}