Skip to content

Identify hosts in multi-node panels - #63

Draft
kapantzak wants to merge 2 commits into
masterfrom
fix-ticket-835
Draft

Identify hosts in multi-node panels#63
kapantzak wants to merge 2 commits into
masterfrom
fix-ticket-835

Conversation

@kapantzak

Copy link
Copy Markdown
Contributor

Problem

A panel covering several nodes could not show which host each series belonged to. Reported in support ticket 835: disk usage of / across 13 hosts produced 13 indistinguishable series.

The series name came from result.labels, which carries machine-readable ids, and the only translation was a machine-GUID lookup against summary.nodes:

// src/datasource.ts (before)
fields: response.data.result.labels.map((id: string, i: number) => {
  const node = response.data.summary.nodes.find((n: any) => n.mg === id);
  return { name: node?.nm || id, ... };
})

That lookup only matches when grouping by node (where the id is the machine GUID). Grouping by a label makes the id the label value, so every host came back as /. On top of that, Grouping by was single-select, so "per node and per mount point" was not expressible, and no Grafana field labels were emitted, so legend overrides had nothing to work with.

What the API already provides

Verified against netdata/cloud-charts-service (internal/model/data.go, internal/aggregation/data_v2_metadata.go) and a recorded response fixture in that repo:

Field Content
result.labels ["time", ...ids] — machine-readable
view.dimensions.names human-readable names, index-aligned with the above minus "time"
view.dimensions.labels { "<key>": [[values], ...] }, same alignment — only when the request sends the group-by-labels option

aggregations.metrics[].group_by accepts combined values (["node","label"]) alongside group_by_label.

Changes

  • Name series from view.dimensions.names instead of the raw ids (src/shared/utils/series.ts).
  • Attach Field.labels from view.dimensions.labels plus the node identity, so {{node}} legend overrides, joins and transformations work.
  • Send the group-by-labels option, without which those labels are absent.
  • Allow several groupings per query — Grouping by is now a multi-select mapping to group_by / group_by_label (src/shared/utils/grouping.ts).
  • Keep accepting the single-string groupBy written by already saved dashboards (normalizeGroupBy), so no dashboard migration is needed.
  • Scope the chart-data event per query (CHART_DATA.<refId>), so sibling editor rows stop overwriting each other's dimension and filter option lists.

Netdata joins the group-by parts of an id with , (and @ for instance@machine_guid) without escaping, so ids cannot be split back apart reliably. Label values are therefore read straight from view.dimensions.labels, and the node is found by matching any separated part against a known machine GUID.

Result

One query — Nodes = all 13, Grouping by = node + mount_point, Filter by mount_point = / — yields 13 series named /,web01/,web13, each carrying {mount_point: "/", node: "web01"}.

Verification

yarn typecheck  → 0 errors
yarn lint       → 0 errors (13 pre-existing deprecation warnings)
yarn build      → ok
yarn test:ci    → 20 passed / 20 in 4 new suites

Notes for reviewers

  • Not yet exercised against a live space. The response contract is verified from the Cloud service source and a recorded fixture, but this has not been run against real data yet. That is the remaining validation step before this leaves draft.
  • Legend text changes for existing dashboards: raw GUIDs and bare label values become hostnames. Panel overrides keyed on the old strings will need re-pointing. A CHANGELOG entry is probably warranted — not added here.
  • Pre-existing test failures, untouched by this branch: the 5 src/shared/hooks/useFetch*.test.ts suites fail with SyntaxError: Unexpected token 'export', a jest ESM-transform issue via marked@grafana/data. Left alone deliberately; it is why buildGrouping lives in its own dependency-free module rather than being exported from the hook.
  • Grouping by uses MultiSelect rather than Select isMulti, which is what typechecks correctly for a multi-value handler.

Series were named from result.labels, which carries machine-readable ids,
with a node lookup that only applied when grouping by node. Grouping by a
label produced the same legend entry for every host (e.g. "/"), so a panel
covering several nodes could not be read.

- name series from view.dimensions.names, the human-readable counterpart
  that is index-aligned with result.labels
- attach Grafana field labels from view.dimensions.labels plus the node
  identity, so {{node}} legend overrides and transformations work
- request the group-by-labels option, without which those labels are absent
  from the response
- allow several groupings in one query, so a single query can separate
  series per node and per label at the same time
- keep accepting the single-string groupBy of already saved dashboards
- scope the chart-data event per query, so sibling editor rows stop
  overwriting each other's dimension and filter options
Grouping by dimension leaves the node out of the series id, so a query
pinned to a single node produced a legend with no way to tell which host
it came from - two such queries in one panel were identical.

Fall back to the only node in summary.nodes when the id carries none: with
one node in scope every series belongs to it, while several nodes mean the
series really is a cross-node aggregate and stays unlabelled.

summary.nodes is the only valid source - agents names whichever agent
served the query, which is routinely a different node.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant