From patchwork Wed Aug 12 13:07:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timothy Redaelli X-Patchwork-Id: 2284416 X-Patchwork-Delegate: echaudro@redhat.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=WZhmZK2i; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4hKphc6v2Zz1xsQ for ; Wed, 12 Aug 2026 23:08:32 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 65D6D81277; Wed, 12 Aug 2026 13:08:31 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id JnfrKcdvrIFb; Wed, 12 Aug 2026 13:08:29 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org ADF628126C Authentication-Results: smtp1.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=WZhmZK2i Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id ADF628126C; Wed, 12 Aug 2026 13:08:29 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 85F44C03FF; Wed, 12 Aug 2026 13:08:29 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 37E21C0013 for ; Wed, 12 Aug 2026 13:08:29 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 225DB40919 for ; Wed, 12 Aug 2026 13:08:29 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id kyNdT-4V30Yx for ; Wed, 12 Aug 2026 13:08:24 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=tredaelli@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp4.osuosl.org B07D0408BA Authentication-Results: smtp4.osuosl.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org B07D0408BA Authentication-Results: smtp4.osuosl.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=WZhmZK2i Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp4.osuosl.org (Postfix) with ESMTP id B07D0408BA for ; Wed, 12 Aug 2026 13:08:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1786540101; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DJgZm3KAbZ3yOCkHDTyq+RAc/i1Lpu/MXG39VicaD/I=; b=WZhmZK2iV6pLk8WETSpe7f0/MQZLQc5FGBFbquTW532Fu1Xsl4a0Muz4m4U0QZlJNaqmz4 oV0aoLKKHZ4+zFdqUaKF1C9EEYqv016FqT1EsnZ6kABB4pqrSB57K31TRzUgJM/mP6hmlX fGILRfgYvbnlNJPiQAKUUp7i7EuEFyc= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-16-IH2AlPx4PoyVmTeHmvLAmQ-1; Wed, 12 Aug 2026 09:08:12 -0400 X-MC-Unique: IH2AlPx4PoyVmTeHmvLAmQ-1 X-Mimecast-MFC-AGG-ID: IH2AlPx4PoyVmTeHmvLAmQ_1786540091 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B26CA195606D; Wed, 12 Aug 2026 13:08:11 +0000 (UTC) Received: from aldebaran.drizzt.lan (unknown [10.44.49.243]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 67C22180049F; Wed, 12 Aug 2026 13:08:10 +0000 (UTC) To: dev@openvswitch.org Date: Wed, 12 Aug 2026 15:07:53 +0200 Message-ID: <25e9d06060c0421f1a29b8de9d3149f618f1fb74.1786481318.git.tredaelli@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ZBZR9dpdKf3T4RRRAldnzPVc3m2wr-ZBCVH020Bf6ik_1786540091 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH v5 5/5] dpctl: Add JSON output to dpctl/show. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Timothy Redaelli via dev From: Timothy Redaelli Reply-To: Timothy Redaelli Cc: Ilya Maximets Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" When --format json is passed to ovs-appctl, dpctl/show returns a JSON object keyed by datapath name. Each entry contains "flows", "lookups" (hit/lost/missed), and "ports" (keyed by port name, each with "port-number" and "type"). The optional sections "masks" and "cache" are only reported when the datapath supports them, and so are the port "config" and "statistics" sub-objects. "cache" contains "statistics" (hits and hit rate) and "config", an array with the name and the size of every cache level. The output_format field is added to struct dpctl_params so the handler can select the appropriate callback (show_dpif or show_dpif_json). The JSON accumulator is stored in dpctl_params.json, and the reply is sent by dpctl_unixctl_handler based on whether json is set. On error, the JSON object is discarded and a text error reply is sent. Since the JSON error path reuses the text error string, a failure of dps_for_each() to enumerate the datapaths is now reported instead of replying with an empty message. Example output: {"ovs-system": {"flows": 0, "lookups": {"hit": 0, "lost": 0, "missed": 0}, "ports": {"br0": {"port-number": 0, "type": "internal"}}}} Signed-off-by: Timothy Redaelli --- NEWS | 1 + lib/dpctl.c | 216 +++++++++++++++++++++++++++++++++++++++- lib/dpctl.h | 11 ++ lib/dpctl.man | 8 ++ tests/dpctl.at | 88 ++++++++++++++++ tests/system-traffic.at | 31 ++++++ 6 files changed, 350 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 845b0798b..6c9fc6492 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Post-v4.0.0 'dpif-netdev/pmd-sleep-show'. * Added JSON output support (--format json) for 'dpif-netdev/pmd-perf-show'. + * Added JSON output support (--format json) for 'dpctl/show'. v4.0.0 - xx xxx xxxx diff --git a/lib/dpctl.c b/lib/dpctl.c index d6ff348e5..bd96519ed 100644 --- a/lib/dpctl.c +++ b/lib/dpctl.c @@ -37,6 +37,7 @@ #include "dpif-provider.h" #include "openvswitch/dynamic-string.h" #include "flow.h" +#include "openvswitch/json.h" #include "openvswitch/match.h" #include "netdev.h" #include "netlink.h" @@ -635,6 +636,189 @@ show_dpif_cache(struct dpif *dpif, struct dpctl_params *dpctl_p) show_dpif_cache__(dpif, dpctl_p); } +/* Adds 'value' to 'json' under 'name', unless the statistic is not + * supported by the netdev, which is indicated by UINT64_MAX. */ +static void +json_put_stat(struct json *json, const char *name, uint64_t value) +{ + if (value != UINT64_MAX) { + json_object_put(json, name, json_integer_create(value)); + } +} + +static struct json * +netdev_stats_to_json(const struct netdev_stats *s) +{ + struct json *json = json_object_create(); + + json_put_stat(json, "collisions", s->collisions); + json_put_stat(json, "rx-bytes", s->rx_bytes); + json_put_stat(json, "rx-dropped", s->rx_dropped); + json_put_stat(json, "rx-errors", s->rx_errors); + json_put_stat(json, "rx-frame-errors", s->rx_frame_errors); + json_put_stat(json, "rx-over-errors", s->rx_over_errors); + json_put_stat(json, "rx-packets", s->rx_packets); + json_put_stat(json, "tx-aborted-errors", s->tx_aborted_errors); + json_put_stat(json, "tx-bytes", s->tx_bytes); + json_put_stat(json, "tx-carrier-errors", s->tx_carrier_errors); + json_put_stat(json, "tx-dropped", s->tx_dropped); + json_put_stat(json, "tx-errors", s->tx_errors); + json_put_stat(json, "tx-packets", s->tx_packets); + json_put_stat(json, "upcall-errors", s->upcall_errors); + json_put_stat(json, "upcall-packets", s->upcall_packets); + + return json; +} + +static void +show_dpif_json(struct dpif *dpif, struct dpctl_params *dpctl_p) +{ + struct json *json_ports = json_object_create(); + size_t allocated_port_nos = 0, n_port_nos = 0; + struct json *json_dp = json_object_create(); + struct json *json_dps = dpctl_p->json; + struct json *json_cache = NULL; + odp_port_t *port_nos = NULL; + struct dpif_port_dump dump; + struct dpif_dp_stats stats; + struct dpif_port dpif_port; + uint32_t nr_caches; + + if (!dpif_get_dp_stats(dpif, &stats)) { + struct json *json_lookups = json_object_create(); + uint64_t n_pkts = stats.n_hit + stats.n_missed; + + json_object_put(json_lookups, "hit", + json_integer_create(stats.n_hit)); + json_object_put(json_lookups, "lost", + json_integer_create(stats.n_lost)); + json_object_put(json_lookups, "missed", + json_integer_create(stats.n_missed)); + json_object_put(json_dp, "flows", json_integer_create(stats.n_flows)); + json_object_put(json_dp, "lookups", json_lookups); + + if (stats.n_masks != UINT32_MAX) { + double avg = n_pkts ? (double) stats.n_mask_hit / n_pkts : 0.0; + struct json *json_masks = json_object_create(); + + json_object_put(json_masks, "hit", + json_integer_create(stats.n_mask_hit)); + json_object_put(json_masks, "hit-per-packet", + json_real_create(avg)); + json_object_put(json_masks, "total", + json_integer_create(stats.n_masks)); + json_object_put(json_dp, "masks", json_masks); + } + + if (stats.n_cache_hit != UINT64_MAX) { + double avg_hits = n_pkts + ? (double) stats.n_cache_hit / n_pkts * 100 : 0.0; + struct json *json_stats = json_object_create(); + + json_object_put(json_stats, "hits", + json_integer_create(stats.n_cache_hit)); + json_object_put(json_stats, "hit-rate", + json_real_create(avg_hits)); + json_cache = json_object_create(); + json_object_put(json_cache, "statistics", json_stats); + } + } + + if (!dpif_cache_get_supported_levels(dpif, &nr_caches) && nr_caches > 0) { + struct json *json_config = json_array_create_empty(); + + for (uint32_t i = 0; i < nr_caches; i++) { + struct json *json_c; + const char *name; + uint32_t size; + + if (dpif_cache_get_name(dpif, i, &name) || + dpif_cache_get_size(dpif, i, &size)) { + continue; + } + + json_c = json_object_create(); + json_object_put_string(json_c, "name", name); + json_object_put(json_c, "size", json_integer_create(size)); + json_array_add(json_config, json_c); + } + + if (!json_cache) { + json_cache = json_object_create(); + } + json_object_put(json_cache, "config", json_config); + } + + if (json_cache) { + json_object_put(json_dp, "cache", json_cache); + } + + DPIF_PORT_FOR_EACH (&dpif_port, &dump, dpif) { + if (n_port_nos >= allocated_port_nos) { + port_nos = x2nrealloc(port_nos, &allocated_port_nos, + sizeof *port_nos); + } + port_nos[n_port_nos++] = dpif_port.port_no; + } + + if (port_nos) { + qsort(port_nos, n_port_nos, sizeof *port_nos, compare_port_nos); + } + + for (int i = 0; i < n_port_nos; i++) { + struct json *json_port; + struct netdev *netdev; + + if (dpif_port_query_by_number(dpif, port_nos[i], &dpif_port, true)) { + continue; + } + + json_port = json_object_create(); + json_object_put(json_port, "port-number", + json_integer_create(odp_to_u32(dpif_port.port_no))); + json_object_put_string(json_port, "type", dpif_port.type); + + if (strcmp(dpif_port.type, "system")) { + int error = netdev_open(dpif_port.name, dpif_port.type, &netdev); + + if (!error) { + struct smap config; + + smap_init(&config); + error = netdev_get_config(netdev, &config); + if (!error && smap_count(&config) > 0) { + json_object_put(json_port, "config", + smap_to_json(&config)); + } + smap_destroy(&config); + netdev_close(netdev); + } + } + + if (dpctl_p->print_statistics) { + int error = netdev_open(dpif_port.name, dpif_port.type, &netdev); + struct netdev_stats s; + + if (!error) { + error = netdev_get_stats(netdev, &s); + + netdev_close(netdev); + if (!error) { + json_object_put(json_port, "statistics", + netdev_stats_to_json(&s)); + } + } + } + + json_object_put(json_ports, dpif_port.name, json_port); + dpif_port_destroy(&dpif_port); + } + + free(port_nos); + json_object_put(json_dp, "ports", json_ports); + json_object_put(json_dps, dpif_name(dpif), json_dp); +} + static void show_dpif(struct dpif *dpif, struct dpctl_params *dpctl_p) { @@ -824,15 +1008,29 @@ dps_for_each(struct dpctl_params *dpctl_p, dps_for_each_cb cb) * is not loaded. */ if (openerror) { return openerror; - } else { - return at_least_one ? 0 : enumerror; } + if (at_least_one) { + return 0; + } + if (enumerror) { + dpctl_error(dpctl_p, enumerror, "enumerating datapaths failed"); + } + return enumerror; } static int dpctl_show(int argc, const char *argv[], struct dpctl_params *dpctl_p) { int error, lasterror = 0; + dps_for_each_cb cb; + + if (dpctl_p->output_format == UNIXCTL_OUTPUT_FMT_JSON) { + dpctl_p->json = json_object_create(); + cb = show_dpif_json; + } else { + cb = show_dpif; + } + if (argc > 1) { int i; for (i = 1; i < argc; i++) { @@ -841,7 +1039,7 @@ dpctl_show(int argc, const char *argv[], struct dpctl_params *dpctl_p) error = parsed_dpif_open(name, false, &dpif); if (!error) { - show_dpif(dpif, dpctl_p); + cb(dpif, dpctl_p); dpif_close(dpif); } else { dpctl_error(dpctl_p, error, "opening datapath %s failed", @@ -850,7 +1048,7 @@ dpctl_show(int argc, const char *argv[], struct dpctl_params *dpctl_p) } } } else { - lasterror = dps_for_each(dpctl_p, show_dpif); + lasterror = dps_for_each(dpctl_p, cb); } return lasterror; @@ -3159,6 +3357,7 @@ dpctl_unixctl_handler(struct unixctl_conn *conn, int argc, const char *argv[], .is_appctl = true, .output = dpctl_unixctl_print, .aux = &ds, + .output_format = unixctl_command_get_output_format(conn), }; /* Parse options (like getopt). Unfortunately it does @@ -3226,7 +3425,14 @@ dpctl_unixctl_handler(struct unixctl_conn *conn, int argc, const char *argv[], error = handler(argc, argv, &dpctl_p) != 0; } - if (error) { + if (dpctl_p.json) { + if (error) { + json_destroy(dpctl_p.json); + unixctl_command_reply_error(conn, ds_cstr(&ds)); + } else { + unixctl_command_reply_json(conn, dpctl_p.json); + } + } else if (error) { unixctl_command_reply_error(conn, ds_cstr(&ds)); } else { unixctl_command_reply(conn, ds_cstr(&ds)); diff --git a/lib/dpctl.h b/lib/dpctl.h index 9d0052152..fe475471e 100644 --- a/lib/dpctl.h +++ b/lib/dpctl.h @@ -19,6 +19,9 @@ #include #include "compiler.h" +#include "unixctl.h" + +struct json; struct dpctl_params { /* True if it is called by ovs-appctl command. */ @@ -51,6 +54,14 @@ struct dpctl_params { /* 'usage' (if != NULL) gets called for the "help" command. */ void (*usage)(void *aux); + + /* Output format requested by the caller. */ + enum unixctl_output_fmt output_format; + + /* JSON object for accumulating structured output. Command handlers + * set this to a non-NULL value when producing JSON output; the caller + * uses it to decide whether to send a JSON or text reply. */ + struct json *json; }; int dpctl_run_command(int argc, const char *argv[], diff --git a/lib/dpctl.man b/lib/dpctl.man index 66fc50903..eb12af2a6 100644 --- a/lib/dpctl.man +++ b/lib/dpctl.man @@ -87,6 +87,14 @@ packets processed by the datapath. If one or more datapaths are specified, information on only those datapaths are displayed. Otherwise, \fB\*(PN\fR displays information about all configured datapaths. +.IP +JSON output is an object keyed by datapath name. Each datapath holds the +"flows" count, a "lookups" object with the "hit", "missed" and "lost" +counters, a "masks" object with the "hit", "total" and "hit-per-packet" +values, a "cache" object with the cache "statistics" and "config", and a +"ports" object keyed by port name. Each port holds its "port-number" and, +where available, its "config" and its "statistics". A row that the text +output omits is left out of the JSON output as well. .SS "DATAPATH FLOW TABLE DEBUGGING COMMANDS" The following commands are primarily useful for debugging Open vSwitch. The flow table entries (both matches and actions) that they diff --git a/tests/dpctl.at b/tests/dpctl.at index a87f67f98..8e590bfdd 100644 --- a/tests/dpctl.at +++ b/tests/dpctl.at @@ -25,6 +25,22 @@ dummy@br0: flows: 0 port 0: br0 (dummy-internal) ]) + +dnl Check dpctl/show JSON output. +AT_CHECK([ovs-appctl --format json --pretty dpctl/show dummy@br0], [0], [dnl +{ + "dummy@br0": { + "flows": 0, + "lookups": { + "hit": 0, + "lost": 0, + "missed": 0}, + "ports": { + "br0": { + "port-number": 0, + "type": "dummy-internal"}}}} +]) + AT_CHECK([ovs-appctl dpctl/add-if dummy@br0 vif1.0,type=dummy,port_no=5]) AT_CHECK([ovs-appctl dpctl/show dummy@br0], [0], [dnl dummy@br0: @@ -33,6 +49,78 @@ dummy@br0: port 0: br0 (dummy-internal) port 5: vif1.0 (dummy) ]) + +dnl Check that netdev configuration shows up in the JSON output. +AT_CHECK([ovs-appctl dpctl/add-if dummy@br0 vif2.0,type=dummy,ifindex=42,port_no=6]) +AT_CHECK([ovs-appctl --format json --pretty dpctl/show dummy@br0], [0], [dnl +{ + "dummy@br0": { + "flows": 0, + "lookups": { + "hit": 0, + "lost": 0, + "missed": 0}, + "ports": { + "br0": { + "port-number": 0, + "type": "dummy-internal"}, + "vif1.0": { + "port-number": 5, + "type": "dummy"}, + "vif2.0": { + "config": { + "ifindex": "42"}, + "port-number": 6, + "type": "dummy"}}}} +]) + +dnl Check port statistics in the JSON output. Dummy netdevs only collect +dnl rx/tx packet and byte counters; unavailable counters must be omitted. +AT_CHECK([ovs-appctl --format json --pretty dpctl/show -s dummy@br0], [0], [dnl +{ + "dummy@br0": { + "flows": 0, + "lookups": { + "hit": 0, + "lost": 0, + "missed": 0}, + "ports": { + "br0": { + "port-number": 0, + "statistics": { + "rx-bytes": 0, + "rx-packets": 0, + "tx-bytes": 0, + "tx-packets": 0}, + "type": "dummy-internal"}, + "vif1.0": { + "port-number": 5, + "statistics": { + "rx-bytes": 0, + "rx-packets": 0, + "tx-bytes": 0, + "tx-packets": 0}, + "type": "dummy"}, + "vif2.0": { + "config": { + "ifindex": "42"}, + "port-number": 6, + "statistics": { + "rx-bytes": 0, + "rx-packets": 0, + "tx-bytes": 0, + "tx-packets": 0}, + "type": "dummy"}}}} +]) + +dnl Check that errors are reported as text even if JSON was requested. +AT_CHECK([ovs-appctl --format json dpctl/show dummy@nodp], [2], [], [stderr]) +AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], [dnl +ovs-vswitchd: opening datapath dummy@nodp failed (...) +ovs-appctl: ovs-vswitchd: server returned an error +]) + +AT_CHECK([ovs-appctl dpctl/del-if dummy@br0 vif2.0]) AT_CHECK([ovs-appctl dpctl/add-if dummy@br0 vif1.0,type=dummy], [2], [], [stderr]) AT_CHECK([sed 's/(.*)/(...)/' stderr], [0], diff --git a/tests/system-traffic.at b/tests/system-traffic.at index 6550f90d9..067a9edb9 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -2524,6 +2524,37 @@ AT_CHECK([ovs-dpctl cache-get-size | grep masks-cache | tr -d [[:blank:]]], [0], masks-cache:size:256 ]) +dnl Check that the mask and cache details are present in the JSON output. +dnl The values depend on the traffic, so only the structure is checked. The +dnl cache statistics are not included, as the datapath reports no cache hits +dnl at this point. +AT_CHECK([ovs-appctl --format json --pretty dpctl/show | dnl + sed 's/: [[0-9]][[0-9.e+-]]*/: /g'], [0], [dnl +{ + "system@ovs-system": { + "cache": { + "config": [[ + { + "name": "masks-cache", + "size": }]]}, + "flows": , + "lookups": { + "hit": , + "lost": , + "missed": }, + "masks": { + "hit": , + "hit-per-packet": , + "total": }, + "ports": { + "br0": { + "port-number": , + "type": "internal"}, + "ovs-system": { + "port-number": , + "type": "internal"}}}} +]) + OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP