On 22/04/2025 10:12, Eelco Chaudron wrote: > > > On 21 Apr 2025, at 9:08, Roi Dayan wrote: > >> On 17/04/2025 13:30, Eelco Chaudron wrote: >>> >>> >>> On 7 Apr 2025, at 12:23, Roi Dayan via dev wrote: >>> >>>> From: Dima Chumak <dchu...@nvidia.com> >>>> >>>> The 'ovs/route/show' command now supports machine-readable JSON output >>>> in addition to the plain-text output for humans. An example would be: >>>> >>>> ovs-appctl --format json --pretty ovs/route/show >>>> [ >>>> { >>>> "is_local": false, >>>> "nw_addr": "::1", >>>> "output_netdev": "lo", >>>> "plen": 128, >>>> "priority": 128, >>>> "src_addr": "::1"}, >>>> { >>>> "is_local": true, >>>> "nw_addr": "10.237.157.103", >>>> "output_netdev": "eth1", >>>> "plen": 128, >>>> "priority": 192, >>>> "src_addr": "10.237.157.103"}, >>>> { >>>> "is_local": true, >>>> "nw_addr": "fe80::42:67ff:fe28:188", >>>> "output_netdev": "docker0", >>>> "plen": 128, >>>> "priority": 192, >>>> "src_addr": "fe80::42:67ff:fe28:188"}, >>>> { >>>> "gw": "192.168.121.1", >>>> "is_local": false, >>>> "nw_addr": "0.0.0.0", >>>> "output_netdev": "eth0", >>>> "plen": 96, >>>> "priority": 96, >>>> "src_addr": "192.168.121.203"}] >>>> >>>> Signed-off-by: Dima Chumak <dchu...@nvidia.com> >>>> Reviewed-by: Roi Dayan <r...@nvidia.com> >>> >>> Hi Dima, thanks for the patch. See some comments below. >>> >>> Cheers, >>> >>> Eelco >>> >>>> --- >>>> lib/ovs-router.c | 103 ++++++++++++++++++++++++++++++++++++-------- >>>> tests/ovs-router.at | 35 +++++++++++++++ >>>> 2 files changed, 119 insertions(+), 19 deletions(-) >>>> >>>> diff --git a/lib/ovs-router.c b/lib/ovs-router.c >>>> index d955a3a543b8..eb04edece9cb 100644 >>>> --- a/lib/ovs-router.c >>>> +++ b/lib/ovs-router.c >>>> @@ -36,6 +36,7 @@ >>>> #include "dpif.h" >>>> #include "fatal-signal.h" >>>> #include "openvswitch/dynamic-string.h" >>>> +#include "openvswitch/json.h" >>>> #include "netdev.h" >>>> #include "packets.h" >>>> #include "seq.h" >>>> @@ -509,44 +510,108 @@ ovs_router_del(struct unixctl_conn *conn, int argc >>>> OVS_UNUSED, >>>> } >>>> >>>> static void >>>> -ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, >>>> - const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) >>>> +ovs_router_show_json(struct json **routes) >>>> { >>>> + struct json **json_entries = NULL; >>>> struct ovs_router_entry *rt; >>>> - struct ds ds = DS_EMPTY_INITIALIZER; >>>> + int i = 0; >>>> >>>> - ds_put_format(&ds, "Route Table:\n"); >>>> - CLS_FOR_EACH(rt, cr, &cls) { >>>> + if (!cls.n_rules) { >>>> + goto out; >>>> + } >>>> + >>>> + json_entries = xmalloc(cls.n_rules * sizeof *json_entries); >>>> + >>>> + CLS_FOR_EACH (rt, cr, &cls) { >>>> + struct json *json = json_object_create(); >>>> + struct ds ds; >>>> + >>> >>> In the text output, we show if it’s a user-configured entry; Maybe we >>> should do the same for json? Not sure about the name, but something like: >>> >>> json_object_put(json, “user_configured”, json_boolean_create((rt->priority >>> != rt->plen && !rt->local))); >> >> added "user". >>> >>>> + json_object_put(json, "is_local", json_boolean_create(rt->local)); >> >> ack. >> >>> >>> I would keep the name "local" as looking at the existing code, we don’t >>> seem to add prefixes for booleans, just the variable name. >>> >>>> + json_object_put(json, "priority", >>>> json_integer_create(rt->priority)); >>>> + json_object_put(json, "plen", json_integer_create(rt->plen)); >>> >>> In the text code the plen is normalized based on the network address type. >>> Should we do the same here? >>> Also, the plen is the network mask, so should we add it to the nw_addr >>> instead? >>> >> >> The purpose for json here is for scripts/sw and not user. so "raw" data will >> be easier to handle >> instead of parsing the string of ip/mask in the script later. > > Then I would at least export it as ‘mask’ so it’s clear what we’re talking > about. >
ack. I'll send v2 for comments. >>>> + json_object_put_string(json, "output_netdev", rt->output_netdev); >>>> + >>>> + ds_init(&ds); >>>> + ipv6_format_mapped(&rt->nw_addr, &ds); >>>> + json_object_put_string(json, "nw_addr", ds_cstr_ro(&ds)); >>>> + ds_destroy(&ds); >>>> + >>>> + ds_init(&ds); >>>> + ipv6_format_mapped(&rt->src_addr, &ds); >>>> + json_object_put_string(json, "src_addr", ds_cstr_ro(&ds)); >>>> + ds_destroy(&ds); >>>> + >>>> + if (rt->mark) { >>>> + json_object_put(json, "mark", json_integer_create(rt->mark)); >>>> + } >>>> + >>>> + if (ipv6_addr_is_set(&rt->gw)) { >>>> + ds_init(&ds); >>>> + ipv6_format_mapped(&rt->gw, &ds); >>>> + json_object_put_string(json, "gw", ds_cstr_ro(&ds)); >>>> + ds_destroy(&ds); >>>> + } >>>> + >>> >>> Rather than calling ds_destroy() each time, consider calling ds_clear() >>> instead, and only call ds_destroy() once at the end. This avoids repeated >>> malloc/free calls. Preferably move it outside the CLS_FOR_EACH() loop. >>> >> >> ack. >> >>>> + json_entries[i++] = json; >>>> + } >>>> + >>>> +out: >>>> + *routes = json_array_create(json_entries, i); >>>> +} >>>> + >>>> +static void >>>> +ovs_router_show_text(struct ds *ds) >>>> +{ >>>> + struct ovs_router_entry *rt; >>>> + >>>> + ds_put_format(ds, "Route Table:\n"); >>>> + CLS_FOR_EACH (rt, cr, &cls) { >>>> uint8_t plen; >>>> if (rt->priority == rt->plen || rt->local) { >>>> - ds_put_format(&ds, "Cached: "); >>>> + ds_put_format(ds, "Cached: "); >>>> } else { >>>> - ds_put_format(&ds, "User: "); >>>> + ds_put_format(ds, "User: "); >>>> } >>>> - ipv6_format_mapped(&rt->nw_addr, &ds); >>>> + ipv6_format_mapped(&rt->nw_addr, ds); >>>> plen = rt->plen; >>>> if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) { >>>> plen -= 96; >>>> } >>>> - ds_put_format(&ds, "/%"PRIu8, plen); >>>> + ds_put_format(ds, "/%"PRIu8, plen); >>>> if (rt->mark) { >>>> - ds_put_format(&ds, " MARK %"PRIu32, rt->mark); >>>> + ds_put_format(ds, " MARK %"PRIu32, rt->mark); >>>> } >>>> >>>> - ds_put_format(&ds, " dev %s", rt->output_netdev); >>>> + ds_put_format(ds, " dev %s", rt->output_netdev); >>>> if (ipv6_addr_is_set(&rt->gw)) { >>>> - ds_put_format(&ds, " GW "); >>>> - ipv6_format_mapped(&rt->gw, &ds); >>>> + ds_put_format(ds, " GW "); >>>> + ipv6_format_mapped(&rt->gw, ds); >>>> } >>>> - ds_put_format(&ds, " SRC "); >>>> - ipv6_format_mapped(&rt->src_addr, &ds); >>>> + ds_put_format(ds, " SRC "); >>>> + ipv6_format_mapped(&rt->src_addr, ds); >>>> if (rt->local) { >>>> - ds_put_format(&ds, " local"); >>>> + ds_put_format(ds, " local"); >>>> } >>>> - ds_put_format(&ds, "\n"); >>>> + ds_put_format(ds, "\n"); >>>> + } >>>> +} >>>> + >>>> +static void >>>> +ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, >>>> + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) >>>> +{ >>>> + if (unixctl_command_get_output_format(conn) == >>>> UNIXCTL_OUTPUT_FMT_JSON) { >>>> + struct json *routes; >>>> + >>>> + ovs_router_show_json(&routes); >>>> + unixctl_command_reply_json(conn, routes); >>>> + } else { >>>> + struct ds ds = DS_EMPTY_INITIALIZER; >>>> + >>>> + ovs_router_show_text(&ds); >>>> + unixctl_command_reply(conn, ds_cstr(&ds)); >>>> + ds_destroy(&ds); >>>> } >>>> - unixctl_command_reply(conn, ds_cstr(&ds)); >>>> - ds_destroy(&ds); >>>> } >>>> >>>> static void >>>> diff --git a/tests/ovs-router.at b/tests/ovs-router.at >>>> index b3314b3dff0d..13a38eb55055 100644 >>>> --- a/tests/ovs-router.at >>>> +++ b/tests/ovs-router.at >>>> @@ -28,6 +28,41 @@ User: 1.1.1.0/24 dev br0 GW 2.2.2.10 SRC 2.2.2.2 >>>> User: 1.1.2.0/24 MARK 2 dev br0 GW 2.2.2.10 SRC 2.2.2.2 >>>> User: 2.2.2.3/32 MARK 1 dev br0 SRC 2.2.2.2 >>>> ]) >>>> +AT_CHECK([ovs-appctl --format=json --pretty ovs/route/show], [0], [dnl >>>> +[[ >>>> + { >>>> + "is_local": true, >>>> + "nw_addr": "2.2.2.0", >>>> + "output_netdev": "br0", >>>> + "plen": 120, >>>> + "priority": 184, >>>> + "src_addr": "2.2.2.2"}, >>> >>> On json naming, should we align a bit more to the ‘ip [—6] -json’ route >>> show output? >>> >>> nw_addr -> dst >>> src_addr -> prefsrc >>> output_netlink -> dev >>> >>> Maybe we should also be prepared for multiple next hops, so if a gw is >>> present, we should do something like the below (for gateway and dev), so we >>> do not have to change the format later; >>> >>> { >>> "dst": "default", >>> "protocol": "ra", >>> "metric": 100, >>> "flags": [], >>> "pref": "medium", >>> "nexthops": [ >>> { >>> "gateway": "fe80::4a5a:d01:9431:5120", >>> "dev": "eno8303", >>> "weight": 1, >>> "flags": [] >>> }, >>> { >>> "gateway": "fe80::4a5a:d01:942f:2b20", >>> "dev": "eno8303", >>> "weight": 1, >>> "flags": [] >>> } >>> ] >>> }, >>> >>> >> >> It's possible but the help string for ovs/route/add uses the attribute >> naming. >> >> ovs/route/add ip/plen output_netdev [gw] [pkt_mark=mark] >> [src=src_ip] >> >> So this was done to be consistent. do you still prefer to update ? >> if so, do we need to udpate the help print? output_netdev to dev, ip to >> nw_addr, src_ip to src_addr.. > > I guess it’s only two parameters that will change, which I think is fine. So > it will become: > > -ovs/route/add ip/plen output_netdev [gw] [pkt_mark=mark] > [src=src_ip] > +ovs/route/add ip/mask dev [gw] [pkt_mark=mark] > [src=src_ip] > ack. >>>> + { >>>> + "gw": "2.2.2.10", >>>> + "is_local": false, >>>> + "nw_addr": "1.1.1.0", >>>> + "output_netdev": "br0", >>>> + "plen": 120, >>>> + "priority": 152, >>>> + "src_addr": "2.2.2.2"}, >>>> + { >>>> + "gw": "2.2.2.10", >>>> + "is_local": false, >>>> + "mark": 2, >>>> + "nw_addr": "1.1.2.0", >>>> + "output_netdev": "br0", >>>> + "plen": 120, >>>> + "priority": 152, >>>> + "src_addr": "2.2.2.2"}, >>>> + { >>>> + "is_local": false, >>>> + "mark": 1, >>>> + "nw_addr": "2.2.2.3", >>>> + "output_netdev": "br0", >>>> + "plen": 128, >>>> + "priority": 160, >>>> + "src_addr": "2.2.2.2"}]] >>>> +]) >>>> OVS_VSWITCHD_STOP >>>> AT_CLEANUP >>>> >>>> -- >>>> 2.21.0 >>>> >>>> _______________________________________________ >>>> dev mailing list >>>> d...@openvswitch.org >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev >>> > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev