On 7/12/24 14:11, Ilya Maximets wrote:
> On 7/12/24 13:54, Dumitru Ceara wrote:
>> It improves the debugging experience if we can easily get a list of
>> OpenFlow rules and groups that contribute to the creation of a datapath
>> flow.
>>
>> The suggested workflow is:
>> a. dump datapath flows (along with UUIDs), this also prints the core IDs
>> (PMD IDs) when applicable.
>>
>> $ ovs-appctl dpctl/dump-flows -m
>> flow-dump from pmd on cpu core: 7
>> ufid:7460db8f..., recirc_id(0), ....
>>
>> b. dump related OpenFlow rules and groups:
>> $ ovs-appctl ofproto/detrace ufid:7460db8f... pmd=7
>> cookie=0x12345678, table=0
>> priority=100,ip,in_port=2,nw_dst=10.0.0.2,actions=resubmit(,1)
>> cookie=0x0, table=1 priority=200,actions=group:1
>> group_id=1,bucket=bucket_id:0,actions=ct(commit,table=2,nat(dst=20.0.0.2))
>> cookie=0x0, table=2 actions=output:1
>>
>> The new command only shows rules and groups attached to ukeys that are
>> in states UKEY_VISIBLE or UKEY_OPERATIONAL. That should be fine as all
>> other ukeys should not be relevant for the use case presented above.
>>
>> This commit tries to mimic the output format of the ovs-ofctl
>> dump-flows/dump-groups commands.
>>
>> Signed-off-by: Dumitru Ceara <[email protected]>
>> ---
>
> A very quick review. Obviously, didn't dive deep, but see a couple
> comments below.
>
>> V5:
>> - Addressed Ilya's comments:
>> - I addressed all from here
>> https://mail.openvswitch.org/pipermail/ovs-dev/2024-June/415144.html
>> except the one about adding -m/-mm. It felt OK to always dump
>> statistics.
>> V4:
>> - Addressed Mike's comment:
>> - use predictable port IDs.
>> V3:
>> - Addressed Mike's comments:
>> - use ds_put_char()
>> - make the test less flaky
>> V2:
>> - Addressed Adrian's comments:
>> - check return value of populate_xcache()
>> - use flow_stats_ds() (renamed to ofproto_rule_stats_ds()) instead of
>> custom printing
>> - move ukey->state check to caller
>> - handle case when group bucket is not available
>> - update test case to cover the point above
>> ---
>> NEWS | 2 +
>> include/openvswitch/ofp-group.h | 14 +++
>> lib/ofp-group.c | 131 +++++++++++++++++------------
>> ofproto/ofproto-dpif-upcall.c | 51 +++++++++++
>> ofproto/ofproto-dpif-xlate-cache.c | 29 +++++++
>> ofproto/ofproto-dpif-xlate-cache.h | 2 +
>> ofproto/ofproto-dpif.c | 11 +++
>> ofproto/ofproto-dpif.h | 2 +
>> ofproto/ofproto-provider.h | 2 +
>> ofproto/ofproto.c | 11 +--
>> tests/ofproto-dpif.at | 62 ++++++++++++++
>> tests/ofproto-macros.at | 14 ++-
>> 12 files changed, 271 insertions(+), 60 deletions(-)
>>
>> diff --git a/NEWS b/NEWS
>> index d18693315eaa..b7eb1baad8eb 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -7,6 +7,8 @@ Post-v3.3.0
>> or 'text' (by default).
>> * Added new option [--pretty] to print JSON output in a readable
>> fashion.
>> * 'dpif/show' and 'list-commands' now support output in JSON format.
>> + * Added 'ofproto/detrace' to output the set of OpenFlow rules and
>> + groups that contributed to the creation of a specific datapath flow.
>> - Userspace datapath:
>> * Conntrack now supports 'random' flag for selecting ports in a range
>> while natting and 'persistent' flag for selection of the IP address
>> diff --git a/include/openvswitch/ofp-group.h
>> b/include/openvswitch/ofp-group.h
>> index cd7af0ebff9c..7cbb2f70f316 100644
>> --- a/include/openvswitch/ofp-group.h
>> +++ b/include/openvswitch/ofp-group.h
>> @@ -70,6 +70,10 @@ struct ofputil_bucket *ofputil_bucket_find(const struct
>> ovs_list *,
>> bool ofputil_bucket_check_duplicate_id(const struct ovs_list *);
>> struct ofputil_bucket *ofputil_bucket_list_front(const struct ovs_list *);
>> struct ofputil_bucket *ofputil_bucket_list_back(const struct ovs_list *);
>> +void ofputil_bucket_format(struct ds *, const struct ofputil_bucket *,
>> + enum ofp11_group_type, enum ofp_version,
>> + const struct ofputil_port_map *,
>> + const struct ofputil_table_map *);
>>
>> static inline bool
>> ofputil_bucket_has_liveness(const struct ofputil_bucket *bucket)
>> @@ -88,6 +92,8 @@ struct ofputil_group_props {
>> void ofputil_group_properties_destroy(struct ofputil_group_props *);
>> void ofputil_group_properties_copy(struct ofputil_group_props *to,
>> const struct ofputil_group_props *from);
>> +void ofputil_group_properties_format(const struct ofputil_group_props *,
>> + struct ds *);
>> /* Protocol-independent group_mod. */
>> struct ofputil_group_mod {
>> uint16_t command; /* One of OFPGC15_*. */
>> @@ -199,6 +205,14 @@ enum ofperr ofputil_group_desc_format(struct ds *,
>> const struct ofp_header *,
>> enum ofperr ofputil_group_features_format(struct ds *,
>> const struct ofp_header *);
>>
>> +/* Group formatting. */
>> +void ofputil_group_format(struct ds *s, uint32_t group_id, uint8_t type,
>> + const struct ofputil_bucket *,
>> + const struct ovs_list *p_buckets,
>> + const struct ofputil_group_props *,
>> + enum ofp_version, bool suppress_type,
>> + const struct ofputil_port_map *,
>> + const struct ofputil_table_map *);
>> #ifdef __cplusplus
>> }
>> #endif
>> diff --git a/lib/ofp-group.c b/lib/ofp-group.c
>> index 737f48047b10..3edf1b01b372 100644
>> --- a/lib/ofp-group.c
>> +++ b/lib/ofp-group.c
>> @@ -1526,6 +1526,31 @@ ofputil_group_properties_destroy(struct
>> ofputil_group_props *gp)
>> free(gp->fields.values);
>> }
>>
>> +void
>> +ofputil_group_properties_format(const struct ofputil_group_props *gp,
>> + struct ds *ds)
>> +{
>> + if (!gp->selection_method[0]) {
>> + return;
>> + }
>> +
>> + ds_put_format(ds, ",selection_method=%s", gp->selection_method);
>> + if (gp->selection_method_param) {
>> + ds_put_format(ds, ",selection_method_param=%"PRIu64,
>> + gp->selection_method_param);
>> + }
>> +
>> + size_t n = bitmap_count1(gp->fields.used.bm, MFF_N_IDS);
>> + if (n == 1) {
>> + ds_put_cstr(ds, ",fields=");
>> + oxm_format_field_array(ds, &gp->fields);
>> + } else if (n > 1) {
>> + ds_put_cstr(ds, ",fields(");
>> + oxm_format_field_array(ds, &gp->fields);
>> + ds_put_char(ds, ')');
>> + }
>> +}
>> +
>> static enum ofperr
>> parse_group_prop_ntr_selection_method(struct ofpbuf *payload,
>> enum ofp11_group_type group_type,
>> @@ -1813,16 +1838,45 @@ ofp_print_bucket_id(struct ds *s, const char *label,
>> uint32_t bucket_id,
>> ds_put_char(s, ',');
>> }
>>
>> -static void
>> -ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
>> - const struct ovs_list *p_buckets,
>> - const struct ofputil_group_props *props,
>> - enum ofp_version ofp_version, bool suppress_type,
>> - const struct ofputil_port_map *port_map,
>> - const struct ofputil_table_map *table_map)
>> +void
>> +ofputil_bucket_format(struct ds * s, const struct ofputil_bucket *bucket,
>> + enum ofp11_group_type type, enum ofp_version
>> ofp_version,
>> + const struct ofputil_port_map *port_map,
>> + const struct ofputil_table_map *table_map)
>> {
>> - struct ofputil_bucket *bucket;
>> + ds_put_cstr(s, "bucket=");
>> +
>> + ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id, ofp_version);
>> + if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) {
>> + ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
>> + }
>> + if (bucket->watch_port != OFPP_NONE) {
>> + ds_put_cstr(s, "watch_port:");
>> + ofputil_format_port(bucket->watch_port, port_map, s);
>> + ds_put_char(s, ',');
>> + }
>> + if (bucket->watch_group != OFPG_ANY) {
>> + ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
>> + }
>>
>> + ds_put_cstr(s, "actions=");
>> + struct ofpact_format_params fp = {
>> + .port_map = port_map,
>> + .table_map = table_map,
>> + .s = s,
>> + };
>> + ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
>> +}
>> +
>> +void
>> +ofputil_group_format(struct ds *s, uint32_t group_id, uint8_t type,
>> + const struct ofputil_bucket *bucket,
>> + const struct ovs_list *p_buckets,
>> + const struct ofputil_group_props *props,
>> + enum ofp_version ofp_version, bool suppress_type,
>> + const struct ofputil_port_map *port_map,
>> + const struct ofputil_table_map *table_map)
>> +{
>> ds_put_format(s, "group_id=%"PRIu32, group_id);
>>
>> if (!suppress_type) {
>> @@ -1831,57 +1885,24 @@ ofp_print_group(struct ds *s, uint32_t group_id,
>> uint8_t type,
>> ds_put_format(s, ",type=%s", type_str[type > 4 ? 4 : type]);
>> }
>>
>> - if (props->selection_method[0]) {
>> - ds_put_format(s, ",selection_method=%s", props->selection_method);
>> - if (props->selection_method_param) {
>> - ds_put_format(s, ",selection_method_param=%"PRIu64,
>> - props->selection_method_param);
>> - }
>> -
>> - size_t n = bitmap_count1(props->fields.used.bm, MFF_N_IDS);
>> - if (n == 1) {
>> - ds_put_cstr(s, ",fields=");
>> - oxm_format_field_array(s, &props->fields);
>> - } else if (n > 1) {
>> - ds_put_cstr(s, ",fields(");
>> - oxm_format_field_array(s, &props->fields);
>> - ds_put_char(s, ')');
>> - }
>> - }
>> + ofputil_group_properties_format(props, s);
>>
>> - if (!p_buckets) {
>> + if (!bucket && !p_buckets) {
>> return;
>> }
>>
>> ds_put_char(s, ',');
>>
>> - LIST_FOR_EACH (bucket, list_node, p_buckets) {
>> - ds_put_cstr(s, "bucket=");
>> -
>> - ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id,
>> ofp_version);
>> - if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) {
>> - ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
>> - }
>> - if (bucket->watch_port != OFPP_NONE) {
>> - ds_put_cstr(s, "watch_port:");
>> - ofputil_format_port(bucket->watch_port, port_map, s);
>> + if (bucket) {
>> + ofputil_bucket_format(s, bucket, type, ofp_version, NULL, NULL);
>> + } else {
>> + LIST_FOR_EACH (bucket, list_node, p_buckets) {
>> + ofputil_bucket_format(s, bucket, type, ofp_version,
>> + port_map, table_map);
>> ds_put_char(s, ',');
>> }
>> - if (bucket->watch_group != OFPG_ANY) {
>> - ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
>> - }
>> -
>> - ds_put_cstr(s, "actions=");
>> - struct ofpact_format_params fp = {
>> - .port_map = port_map,
>> - .table_map = table_map,
>> - .s = s,
>> - };
>> - ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
>> - ds_put_char(s, ',');
>> + ds_chomp(s, ',');
>> }
>> -
>> - ds_chomp(s, ',');
>> }
>>
>> enum ofperr
>> @@ -1901,8 +1922,9 @@ ofputil_group_desc_format(struct ds *s, const struct
>> ofp_header *oh,
>>
>> ds_put_char(s, '\n');
>> ds_put_char(s, ' ');
>> - ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, &gd.props,
>> - oh->version, false, port_map, table_map);
>> + ofputil_group_format(s, gd.group_id, gd.type, NULL, &gd.buckets,
>> + &gd.props, oh->version, false,
>> + port_map, table_map);
>> ofputil_uninit_group_desc(&gd);
>> }
>> }
>> @@ -2368,8 +2390,9 @@ ofputil_group_mod_format__(struct ds *s, enum
>> ofp_version ofp_version,
>> gm->command_bucket_id, ofp_version);
>> }
>>
>> - ofp_print_group(s, gm->group_id, gm->type, &gm->buckets, &gm->props,
>> - ofp_version, bucket_command, port_map, table_map);
>> + ofputil_group_format(s, gm->group_id, gm->type, NULL, &gm->buckets,
>> + &gm->props, ofp_version, bucket_command,
>> + port_map, table_map);
>> }
>>
>> enum ofperr
>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>> index 83609ec62b63..2054f1c056e5 100644
>> --- a/ofproto/ofproto-dpif-upcall.c
>> +++ b/ofproto/ofproto-dpif-upcall.c
>> @@ -383,6 +383,7 @@ static void upcall_unixctl_disable_ufid(struct
>> unixctl_conn *, int argc,
>> const char *argv[], void
>> *aux);
>> static void upcall_unixctl_enable_ufid(struct unixctl_conn *, int argc,
>> const char *argv[], void *aux);
>> +
>> static void upcall_unixctl_set_flow_limit(struct unixctl_conn *conn, int
>> argc,
>> const char *argv[], void *aux);
>> static void upcall_unixctl_dump_wait(struct unixctl_conn *conn, int argc,
>> @@ -394,6 +395,9 @@ static void upcall_unixctl_pause(struct unixctl_conn
>> *conn, int argc,
>> static void upcall_unixctl_resume(struct unixctl_conn *conn, int argc,
>> const char *argv[], void *aux);
>>
>> +static void upcall_unixctl_ofproto_detrace(struct unixctl_conn *, int argc,
>> + const char *argv[], void *aux);
>> +
>> static struct udpif_key *ukey_create_from_upcall(struct upcall *,
>> struct flow_wildcards *);
>> static int ukey_create_from_dpif_flow(const struct udpif *,
>> @@ -470,6 +474,8 @@ udpif_init(void)
>> upcall_unixctl_pause, NULL);
>> unixctl_command_register("revalidator/resume", NULL, 0, 0,
>> upcall_unixctl_resume, NULL);
>> + unixctl_command_register("ofproto/detrace", "UFID PMD-ID", 1, 2,
>> + upcall_unixctl_ofproto_detrace, NULL);
>> ovsthread_once_done(&once);
>> }
>> }
>> @@ -3310,6 +3316,51 @@ upcall_unixctl_resume(struct unixctl_conn *conn, int
>> argc OVS_UNUSED,
>> unixctl_command_reply(conn, "");
>> }
>>
>> +static void
>> +upcall_unixctl_ofproto_detrace(struct unixctl_conn *conn, int argc,
>> + const char *argv[], void *aux OVS_UNUSED)
>> +{
>> + unsigned int pmd_id = NON_PMD_CORE_ID;
>> + const char *key_s = argv[1];
>> + ovs_u128 ufid;
>> +
>> + if (odp_ufid_from_string(key_s, &ufid) <= 0) {
>> + unixctl_command_reply_error(conn, "failed to parse ufid");
>> + return;
>> + }
>> +
>> + if (argc == 3) {
>> + const char *pmd_str = argv[2];
>> + if (!ovs_scan(pmd_str, "pmd=%d", &pmd_id)) {
>> + unixctl_command_reply_error(conn,
>> + "Invalid pmd argument format. "
>> + "Expecting 'pmd=PMD-ID'");
>> + return;
>> + }
>> + }
>> +
>> + struct ds ds = DS_EMPTY_INITIALIZER;
>> + struct udpif *udpif;
>> +
>> + LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
>> + struct udpif_key *ukey = ukey_lookup(udpif, &ufid, pmd_id);
>> + if (!ukey) {
>> + continue;
>> + }
>> +
>> + ovs_mutex_lock(&ukey->mutex);
>> + /* It only makes sense to format rules for ukeys that are (still)
>> + * in use. */
>> + if ((ukey->state == UKEY_VISIBLE || ukey->state == UKEY_OPERATIONAL)
>> + && ukey->xcache) {
>> + xlate_xcache_format(&ds, ukey->xcache);
>> + }
>> + ovs_mutex_unlock(&ukey->mutex);
>> + }
>> + unixctl_command_reply(conn, ds_cstr(&ds));
>> + ds_destroy(&ds);
>> +}
>> +
>>
>> /* Flows are sorted in the following order:
>> * netdev, flow state (offloaded/kernel path), flow_pps_rate.
>> diff --git a/ofproto/ofproto-dpif-xlate-cache.c
>> b/ofproto/ofproto-dpif-xlate-cache.c
>> index 2e1fcb3a6f7a..e987ca8a4636 100644
>> --- a/ofproto/ofproto-dpif-xlate-cache.c
>> +++ b/ofproto/ofproto-dpif-xlate-cache.c
>> @@ -301,3 +301,32 @@ xlate_cache_steal_entries(struct xlate_cache *dst,
>> struct xlate_cache *src)
>> memcpy(p, src_entries->data, src_entries->size);
>> ofpbuf_clear(src_entries);
>> }
>> +
>> +void
>> +xlate_xcache_format(struct ds *s, const struct xlate_cache *xcache)
>> +{
>> + struct ofpbuf entries = xcache->entries;
>> + struct xc_entry *entry;
>> +
>> + XC_ENTRY_FOR_EACH (entry, &entries) {
>> + switch (entry->type) {
>> + case XC_RULE:
>> + ofproto_rule_stats_ds(s, &entry->rule->up, true);
>> + break;
>> + case XC_GROUP:
>> + group_dpif_format(s, entry->group.group, entry->group.bucket);
>> + break;
>> + case XC_TABLE:
>> + case XC_BOND:
>> + case XC_NETDEV:
>> + case XC_NETFLOW:
>> + case XC_MIRROR:
>> + case XC_LEARN:
>> + case XC_NORMAL:
>> + case XC_FIN_TIMEOUT:
>> + case XC_TNL_NEIGH:
>> + case XC_TUNNEL_HEADER:
>> + break;
>> + }
>> + }
>> +}
>> diff --git a/ofproto/ofproto-dpif-xlate-cache.h
>> b/ofproto/ofproto-dpif-xlate-cache.h
>> index 0fc6d2ea60ce..e701734d796a 100644
>> --- a/ofproto/ofproto-dpif-xlate-cache.h
>> +++ b/ofproto/ofproto-dpif-xlate-cache.h
>> @@ -151,4 +151,6 @@ void xlate_cache_uninit(struct xlate_cache *);
>> void xlate_cache_delete(struct xlate_cache *);
>> void xlate_cache_steal_entries(struct xlate_cache *, struct xlate_cache *);
>>
>> +void xlate_xcache_format(struct ds *, const struct xlate_cache *);
>> +
>> #endif /* ofproto-dpif-xlate-cache.h */
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index 87dfb0043dd9..ff94532dbbbb 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -5408,6 +5408,17 @@ group_dpif_lookup(struct ofproto_dpif *ofproto,
>> uint32_t group_id,
>> version, take_ref);
>> return ofgroup ? group_dpif_cast(ofgroup) : NULL;
>> }
>> +
>> +void
>> +group_dpif_format(struct ds *ds, struct group_dpif *group,
>> + struct ofputil_bucket *bucket)
>> +{
>> + struct ofgroup *ofg = &group->up;
>> +
>> + ofputil_group_format(ds, ofg->group_id, ofg->type,
>> + bucket, &ofg->buckets, &ofg->props, OFP15_VERSION,
>> + false, NULL, NULL);
>> +}
>
> Any reason for this function to exist?
> Can xlate_xcache_format() just call ofputil_group_format() directly?
>
It can, I wasn't sure about how much of the internals of ofgroup we
should acces from xlate_xcache_format(). I'll remove it.
>>
>> /* Sends 'packet' out 'ofport'. If 'port' is a tunnel and that tunnel type
>> * supports a notion of an OAM flag, sets it if 'oam' is true.
>> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
>> index d33f73df8aed..acec2c08a3af 100644
>> --- a/ofproto/ofproto-dpif.h
>> +++ b/ofproto/ofproto-dpif.h
>> @@ -151,6 +151,8 @@ void group_dpif_credit_stats(struct group_dpif *,
>> struct group_dpif *group_dpif_lookup(struct ofproto_dpif *,
>> uint32_t group_id, ovs_version_t
>> version,
>> bool take_ref);
>> +void group_dpif_format(struct ds *, struct group_dpif *,
>> + struct ofputil_bucket *);
>>
>>
>> /* Backers.
>> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
>> index 83c509fcf804..4f42cc02df64 100644
>> --- a/ofproto/ofproto-provider.h
>> +++ b/ofproto/ofproto-provider.h
>> @@ -450,6 +450,8 @@ void ofproto_rule_ref(struct rule *);
>> bool ofproto_rule_try_ref(struct rule *);
>> void ofproto_rule_unref(struct rule *);
>>
>> +void ofproto_rule_stats_ds(struct ds *, struct rule *, bool offload_stats);
>> +
>> static inline const struct rule_actions * rule_get_actions(const struct
>> rule *);
>> static inline bool rule_is_table_miss(const struct rule *);
>> static inline bool rule_is_hidden(const struct rule *);
>> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
>> index 21c6a1d8257d..8ad5712f5fc6 100644
>> --- a/ofproto/ofproto.c
>> +++ b/ofproto/ofproto.c
>> @@ -4844,9 +4844,9 @@ handle_flow_stats_request(struct ofconn *ofconn,
>> return 0;
>> }
>>
>> -static void
>> -flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds
>> *results,
>> - bool offload_stats)
>> +void
>> +ofproto_rule_stats_ds(struct ds *results, struct rule *rule,
>> + bool offload_stats)
>> {
>> struct pkt_stats stats;
>> const struct rule_actions *actions;
>> @@ -4875,7 +4875,8 @@ flow_stats_ds(struct ofproto *ofproto, struct rule
>> *rule, struct ds *results,
>> ds_put_format(results, "n_offload_bytes=%"PRIu64", ",
>> stats.n_offload_bytes);
>> }
>> - cls_rule_format(&rule->cr, ofproto_get_tun_tab(ofproto), NULL, results);
>> + cls_rule_format(&rule->cr, ofproto_get_tun_tab(rule->ofproto), NULL,
>> + results);
>> ds_put_char(results, ',');
>>
>> ds_put_cstr(results, "actions=");
>> @@ -4897,7 +4898,7 @@ ofproto_get_all_flows(struct ofproto *p, struct ds
>> *results,
>> struct rule *rule;
>>
>> CLS_FOR_EACH (rule, cr, &table->cls) {
>> - flow_stats_ds(p, rule, results, offload_stats);
>> + ofproto_rule_stats_ds(results, rule, offload_stats);
>> }
>> }
>> }
>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>> index 30ef0468c8d1..7e06c6e3503a 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -12176,3 +12176,65 @@ AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap
>> | wc -l`])
>>
>> OVS_VSWITCHD_STOP
>> AT_CLEANUP
>> +
>> +AT_SETUP([ofproto-dpif - Dump OF rules corresponding to UFID])
>> +OVS_VSWITCHD_START(
>> +[add-port br0 p1 \
>> + -- set bridge br0 datapath-type=dummy \
>> + -- set interface p1 ofport_request=1 type=dummy \
>> + -- add-port br0 p2 \
>> + -- set interface p2 ofport_request=2 type=dummy\
>> + -- add-port br0 p3 \
>> + -- set interface p3 ofport_request=3 type=dummy
>> +], [], [], [DUMMY_NUMA])
>
> You don't need dummy numa here. But also the whole thing can
Copy+paste again, sorry.
> be replaced with:
>
> OVS_VSWITCHD_START
>
> add_of_ports br0 1 2 3
>
Nice, I didn't know that one.
v6 posted:
https://patchwork.ozlabs.org/project/openvswitch/patch/[email protected]/
Regards,
Dumitru
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev