feat: add interface traffic counters as fallback telemetry when conntrack flows unavailable
Add interface_traffic collection in agent to aggregate VM/LXC network counters by vmid/nic with tap/fwln/fwpr/fwbr interface ranking, implement collect_interface_traffic to select best interface per VM NIC and format as flow-like records with rx/tx bytes/packets, add collect_flow_diagnostics to capture conntrack binary path and kernel bridge/netfilter settings for debugging, update workload_insights endpoint
This commit is contained in:
@@ -32,7 +32,11 @@ export function Nodes() {
|
||||
|
||||
function agentFlows(node: Node) {
|
||||
const flows = node.agent?.last_payload?.flows;
|
||||
return Array.isArray(flows) ? flows : [];
|
||||
const interfaceTraffic = node.agent?.last_payload?.interface_traffic;
|
||||
if (Array.isArray(flows) && flows.length) {
|
||||
return flows;
|
||||
}
|
||||
return Array.isArray(interfaceTraffic) ? interfaceTraffic : [];
|
||||
}
|
||||
|
||||
function agentInterfaces(node: Node) {
|
||||
@@ -166,13 +170,21 @@ export function Nodes() {
|
||||
<div className="max-h-48 overflow-auto rounded-md border border-border">
|
||||
{agentFlows(detailNode).length ? agentFlows(detailNode).slice(0, 50).map((item, index) => {
|
||||
const flow = item as Record<string, unknown>;
|
||||
const isInterfaceCounter = flow.protocol === "interface-counter";
|
||||
return (
|
||||
<div key={index} className="border-b border-border px-3 py-2 text-xs last:border-0">
|
||||
<div className="font-medium">{String(flow.source_ip)}:{String(flow.source_port ?? "")} {"->"} {String(flow.destination_ip)}:{String(flow.destination_port ?? "")}</div>
|
||||
<div className="text-slate-500">{String(flow.protocol ?? "unknown")} · {String(flow.bytes ?? 0)} bytes · {String(flow.packets ?? 0)} packets · {String(flow.state ?? "unknown")}</div>
|
||||
<div className="font-medium">
|
||||
{isInterfaceCounter
|
||||
? `VMID ${String(flow.vmid)} ${String(flow.interface ?? "")}`
|
||||
: `${String(flow.source_ip)}:${String(flow.source_port ?? "")} -> ${String(flow.destination_ip)}:${String(flow.destination_port ?? "")}`}
|
||||
</div>
|
||||
<div className="text-slate-500">
|
||||
{String(flow.protocol ?? "unknown")} · {String(flow.bytes ?? 0)} bytes · {String(flow.packets ?? 0)} packets · {String(flow.state ?? "unknown")}
|
||||
</div>
|
||||
{isInterfaceCounter ? <div className="text-slate-500">rx {String(flow.rx_bytes ?? 0)} · tx {String(flow.tx_bytes ?? 0)}</div> : null}
|
||||
</div>
|
||||
);
|
||||
}) : <div className="p-3 text-xs text-slate-500">No conntrack flows were reported in the last heartbeat.</div>}
|
||||
}) : <div className="p-3 text-xs text-slate-500">No conntrack flows or interface counters were reported in the last heartbeat.</div>}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
|
||||
@@ -63,7 +63,9 @@ export function Workloads() {
|
||||
<div key={index} className="rounded-md border border-border p-3">
|
||||
<div>{String(flow.source)} → {String(flow.destination)}</div>
|
||||
<div className="text-xs text-slate-500">{String(flow.protocol)}:{String(flow.port)} · {String(flow.bytes)} bytes · {String(flow.decision)}</div>
|
||||
{flow.protocol === "interface-counter" ? <div className="mt-1 text-xs text-slate-500">Interface: {String(flow.interface ?? "unknown")} · rx {String(flow.rx_bytes ?? 0)} · tx {String(flow.tx_bytes ?? 0)}</div> : null}
|
||||
{Array.isArray(flow.ip_addresses) ? <div className="mt-1 text-xs text-slate-500">IPs: {flow.ip_addresses.join(", ")}</div> : null}
|
||||
{flow.note ? <div className="mt-1 text-xs text-slate-500">{String(flow.note)}</div> : null}
|
||||
</div>
|
||||
)) : <div className="rounded-md border border-border p-3 text-xs text-slate-500">No real traffic telemetry has been collected yet. Install the node agent or enable a flow source to populate this section.</div>}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user