On Tue, Oct 21, 2025 at 8:03 PM Mairtin O'Loingsigh via dev < [email protected]> wrote:
> Add support for transit routers and ovn-ic-nbctl commands to manage > trasnit routers. When created, a transit router creates logical routers in > each AZ > with the same global datapath binding. > > This series also add support for commands to manage transit routers. > > Commands to add, delete and list transit routers. > $ ovn-ic-nbctl tr-add tr0 > $ ovn-ic-nbctl tr-del tr0 > $ ovn-ic-nbctl tr-list > > Signed-off-by: Mairtin O'Loingsigh <[email protected]> > --- > Hi Mairtin, I think we are getting close, just some last small comments. > NEWS | 4 + > ic/ovn-ic.c | 169 ++++++++++++++++++++++++++++++++++----- > ovn-architecture.7.xml | 11 ++- > tests/ovn-ic-nbctl.at | 36 +++++++++ > tests/ovn-ic.at | 32 ++++++++ > utilities/ovn-ic-nbctl.c | 132 +++++++++++++++++++++++++++++- > 6 files changed, 355 insertions(+), 29 deletions(-) > > diff --git a/NEWS b/NEWS > index fb4d63194..593af397d 100644 > --- a/NEWS > +++ b/NEWS > @@ -45,6 +45,10 @@ Post v25.09.0 > enforced). Proper MTU needs to be configured to accomodate this > encapsulation. > > + - Added Transit Router support: > + * Support the creation of Transit Routers. > + * Added new ovn-ic-nbctl 'tr-add','tr-del','tr-list' commands to > manage > + Transit Router. > OVN v25.09.0 - xxx xx xxxx > -------------------------- > - STT tunnels are no longer supported in ovn-encap-type. > diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c > index 304953767..c331910c8 100644 > --- a/ic/ovn-ic.c > +++ b/ic/ovn-ic.c > @@ -90,6 +90,8 @@ struct ic_state { > bool paused; > }; > > +enum ic_datapath_type { IC_SWITCH, IC_ROUTER, IC_DATAPATH_MAX }; > + > static const char *ovnnb_db; > static const char *ovnsb_db; > static const char *ovn_ic_nb_db; > @@ -194,29 +196,49 @@ az_run(struct ic_context *ctx) > } > > static uint32_t > -allocate_ts_dp_key(struct hmap *dp_tnlids, bool vxlan_mode) > +allocate_dp_key(struct hmap *dp_tnlids, bool vxlan_mode, const char *name) > { > uint32_t hint = vxlan_mode ? OVN_MIN_DP_VXLAN_KEY_GLOBAL > : OVN_MIN_DP_KEY_GLOBAL; > Lets add a TODO item about the vxlan_mode handling. That needs to happen too for TRs, but it might be a follow up. - return ovn_allocate_tnlid(dp_tnlids, "transit switch datapath", hint, > + return ovn_allocate_tnlid(dp_tnlids, name, hint, > vxlan_mode ? OVN_MAX_DP_VXLAN_KEY_GLOBAL : > OVN_MAX_DP_KEY_GLOBAL, > &hint); > } > > -static void > -ts_run(struct ic_context *ctx) > +static enum ic_datapath_type > +ic_dp_get_type(const struct icsbrec_datapath_binding *isb_dp) > { > - const struct icnbrec_transit_switch *ts; > - bool dp_key_refresh = false; > + if (isb_dp->type && !strcmp(isb_dp->type, "transit-router")) { > + return IC_ROUTER; > + } > > - struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids); > - struct shash isb_dps = SHASH_INITIALIZER(&isb_dps); > + return IC_SWITCH; > +} > + > +static void > +enumerate_datapaths(struct ic_context *ctx, struct hmap *dp_tnlids, > + struct shash *isb_ts_dps, struct shash *isb_tr_dps) > +{ > const struct icsbrec_datapath_binding *isb_dp; > ICSBREC_DATAPATH_BINDING_FOR_EACH (isb_dp, ctx->ovnisb_idl) { > - shash_add(&isb_dps, isb_dp->transit_switch, isb_dp); > - ovn_add_tnlid(&dp_tnlids, isb_dp->tunnel_key); > + ovn_add_tnlid(dp_tnlids, isb_dp->tunnel_key); > nit: Empty line here would be nice. > + enum ic_datapath_type dp_type = ic_dp_get_type(isb_dp); > + if (dp_type == IC_ROUTER) { > + char *uuid_str = uuid_to_string(isb_dp->nb_ic_uuid); > + shash_add(isb_tr_dps, uuid_str, isb_dp); > + free(uuid_str); > + } else { > + shash_add(isb_ts_dps, isb_dp->transit_switch, isb_dp); > + } > } > +} > > +static void > +ts_run(struct ic_context *ctx, struct hmap *dp_tnlids, > + struct shash *isb_ts_dps) > +{ > + const struct icnbrec_transit_switch *ts; > + bool dp_key_refresh = false; > bool vxlan_mode = false; > const struct icnbrec_ic_nb_global *ic_nb = > icnbrec_ic_nb_global_first(ctx->ovninb_idl); > @@ -266,7 +288,8 @@ ts_run(struct ic_context *ctx) > } > } > > - isb_dp = shash_find_data(&isb_dps, ts->name); > + const struct icsbrec_datapath_binding *isb_dp; > + isb_dp = shash_find_data(isb_ts_dps, ts->name); > if (isb_dp) { > int64_t nb_tnl_key = smap_get_int(&ls->other_config, > "requested-tnl-key", > @@ -274,7 +297,7 @@ ts_run(struct ic_context *ctx) > if (nb_tnl_key != isb_dp->tunnel_key) { > VLOG_DBG("Set other_config:requested-tnl-key %"PRId64 > " for transit switch %s in NB.", > - isb_dp->tunnel_key, ts->name); > + isb_dp->tunnel_key, ls->name); > nit: Unrelated change. > char *tnl_key_str = xasprintf("%"PRId64, > isb_dp->tunnel_key); > nbrec_logical_switch_update_other_config_setkey( > @@ -298,10 +321,12 @@ ts_run(struct ic_context *ctx) > if (ctx->ovnisb_txn) { > /* Create ISB Datapath_Binding */ > ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) { > - isb_dp = shash_find_and_delete(&isb_dps, ts->name); > + const struct icsbrec_datapath_binding *isb_dp = > + shash_find_and_delete(isb_ts_dps, ts->name); > if (!isb_dp) { > /* Allocate tunnel key */ > - int64_t dp_key = allocate_ts_dp_key(&dp_tnlids, > vxlan_mode); > + int dp_key = allocate_dp_key(dp_tnlids, vxlan_mode, > nit: This should remain as int64_t. > + "transit switch datapath"); > if (!dp_key) { > continue; > } > @@ -309,23 +334,106 @@ ts_run(struct ic_context *ctx) > isb_dp = icsbrec_datapath_binding_insert(ctx->ovnisb_txn); > icsbrec_datapath_binding_set_transit_switch(isb_dp, > ts->name); > icsbrec_datapath_binding_set_tunnel_key(isb_dp, dp_key); > + icsbrec_datapath_binding_set_nb_ic_uuid(isb_dp, > + > &ts->header_.uuid, 1); > + icsbrec_datapath_binding_set_type(isb_dp, > "transit-switch"); > We are still not setting the type and nb_ic_uuid for existing rows. This should move down below with two checks: if (!isb_dp->type) { icsbrec_datapath_binding_set_type(isb_dp, "transit-switch"); } if (!iisb_dp->nb_ic_uuid) { icsbrec_datapath_binding_set_nb_ic_uuid(isb_dp, &ts->header_.uuid, 1); } That will ensure it's set for both new ones and for the existing ones too. + ovn_add_tnlid(dp_tnlids, isb_dp->tunnel_key); > + > } else if (dp_key_refresh) { > - /* Refresh tunnel key since encap mode has changhed. */ > - int64_t dp_key = allocate_ts_dp_key(&dp_tnlids, > vxlan_mode); > + /* Refresh tunnel key since encap mode has changed. */ > + int64_t dp_key = allocate_dp_key(dp_tnlids, vxlan_mode, > + "transit switch > datapath"); > if (dp_key) { > + ovn_free_tnlid(dp_tnlids, isb_dp->tunnel_key); > icsbrec_datapath_binding_set_tunnel_key(isb_dp, > dp_key); > } > } > } > + struct shash_node *node; > + SHASH_FOR_EACH (node, isb_ts_dps) { > + const struct icsbrec_datapath_binding *isb_dp = node->data; > + icsbrec_datapath_binding_delete(isb_dp); > We are freeing the tnlid in the TR equivalent down below so it should be aligned, either both of them will free or none will. I'll leave it up to you. + } > + } > +} > + > +static void > +tr_run(struct ic_context *ctx, struct hmap *dp_tnlids, > + struct shash *isb_tr_dps) > +{ > + const struct nbrec_logical_router *lr; > + > + if (ctx->ovnnb_txn) { > + struct shash nb_tres = SHASH_INITIALIZER(&nb_tres); > + NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->ovnnb_idl) { > + const char *tr_name = smap_get(&lr->options, "interconn-tr"); > + if (tr_name) { > + shash_add(&nb_tres, tr_name, lr); > + } > + } > + > + const struct icnbrec_transit_router *tr; > + ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->ovninb_idl) { > + lr = shash_find_and_delete(&nb_tres, tr->name); > + if (!lr) { > + lr = nbrec_logical_router_insert(ctx->ovnnb_txn); > + nbrec_logical_router_set_name(lr, tr->name); > + nbrec_logical_router_update_options_setkey( > + lr, "interconn-tr", tr->name); > + } > + char *uuid_str = uuid_to_string(&tr->header_.uuid); > + struct icsbrec_datapath_binding *isb_dp = shash_find_data( > + isb_tr_dps, uuid_str); > + free(uuid_str); > + > + if (isb_dp) { > + char *tnl_key_str = xasprintf("%"PRId64, > isb_dp->tunnel_key); > + nbrec_logical_router_update_options_setkey( > + lr, "requested-tnl-key", tnl_key_str); > + free(tnl_key_str); > + } > + } > nit: Missing empty space. > + struct shash_node *node; > + SHASH_FOR_EACH (node, &nb_tres) { > + nbrec_logical_router_delete(node->data); > + } > + shash_destroy(&nb_tres); > + } > + > + /* Sync TR between INB and ISB. This is performed after syncing with > AZ > + * SB, to avoid uncommitted ISB datapath tunnel key to be synced back > to > + * AZ. */ > + if (ctx->ovnisb_txn) { > + /* Create ISB Datapath_Binding */ > + const struct icnbrec_transit_router *tr; > + ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->ovninb_idl) { > + char *uuid_str = uuid_to_string(&tr->header_.uuid); > + struct icsbrec_datapath_binding *isb_dp = > + shash_find_and_delete(isb_tr_dps, uuid_str); > + free(uuid_str); > + if (!isb_dp) { > + int dp_key = allocate_dp_key(dp_tnlids, false, > + "transit router datapath"); > + if (!dp_key) { > + continue; > + } > + > + isb_dp = icsbrec_datapath_binding_insert(ctx->ovnisb_txn); > + icsbrec_datapath_binding_set_tunnel_key(isb_dp, dp_key); > + icsbrec_datapath_binding_set_nb_ic_uuid(isb_dp, > + > &tr->header_.uuid, 1); > + icsbrec_datapath_binding_set_type(isb_dp, > "transit-router"); > + } > + } > > - /* Delete extra ISB Datapath_Binding */ > struct shash_node *node; > - SHASH_FOR_EACH (node, &isb_dps) { > + SHASH_FOR_EACH_SAFE (node, isb_tr_dps) { > + struct icsbrec_datapath_binding *isb_dp = node->data; > + ovn_free_tnlid(dp_tnlids, isb_dp->tunnel_key); icsbrec_datapath_binding_delete(node->data); > + shash_delete(isb_tr_dps, node); > No need to do the shash_delete. > } > } > - ovn_destroy_tnlids(&dp_tnlids); > - shash_destroy(&isb_dps); > } > > /* Returns true if any information in gw and chassis is different. */ > @@ -2744,11 +2852,30 @@ update_sequence_numbers(struct ic_context *ctx, > static void > ovn_db_run(struct ic_context *ctx) > { > + struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids); > + struct shash isb_ts_dps = SHASH_INITIALIZER(&isb_ts_dps); > + struct shash isb_tr_dps = SHASH_INITIALIZER(&isb_tr_dps); > + > gateway_run(ctx); > - ts_run(ctx); > + enumerate_datapaths(ctx, &dp_tnlids, &isb_ts_dps, &isb_tr_dps); > + ts_run(ctx, &dp_tnlids, &isb_ts_dps); > + tr_run(ctx, &dp_tnlids, &isb_tr_dps); > port_binding_run(ctx); > route_run(ctx); > sync_service_monitor(ctx); > + > + ovn_destroy_tnlids(&dp_tnlids); > + > + struct shash_node *node; > + SHASH_FOR_EACH_SAFE (node, &isb_ts_dps) { > + shash_delete(&isb_ts_dps, node); > + } > + shash_destroy(&isb_ts_dps); > + > + SHASH_FOR_EACH_SAFE (node, &isb_tr_dps) { > + shash_delete(&isb_tr_dps, node); > + } > No need for the loop in both cases, the shash_destroy is enough. > + shash_destroy(&isb_tr_dps); > } > > static void > diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml > index 11efdf9a2..9a932fef7 100644 > --- a/ovn-architecture.7.xml > +++ b/ovn-architecture.7.xml > @@ -2372,17 +2372,20 @@ > <p> > A global OVN Interconnection Northbound database is introduced for the > operator (probably through CMS systems) to configure transit logical > - switches that connect logical routers from different AZs. A transit > - switch is similar to a regular logical switch, but it is used for > + switches/routers that connect logical routers/switches from different > AZs. > + A transit switch is similar to a regular logical switch, but it is > used for > interconnection purpose only. Typically, each transit switch can be > used > to connect all logical routers that belong to same tenant across all > AZs. > + A transit router is similar to a regular logical router, but is used > for > + redirecting traffic to a specific AZ or cloning a router port across > + multiple AZs. > Maybe slightly better would be to say that Transit router is similar to distributed router, with a bonus that it works across AZs. > </p> > > <p> > A dedicated daemon process <code>ovn-ic</code>, OVN interconnection > controller, in each AZ will consume this data and populate > corresponding > - logical switches to their own northbound databases for each AZ, so > that > - logical routers can be connected to the transit switch by creating > + logical switches/routers to their own northbound databases for each > AZ. So > + that logical routers can be connected to the transit switch by > creating > patch port pairs in their northbound databases. Any router ports > connected to the transit switches are considered interconnection > ports, > which will be exchanged between AZs. > diff --git a/tests/ovn-ic-nbctl.at b/tests/ovn-ic-nbctl.at > index 2402d646c..7aa3a46d8 100644 > --- a/tests/ovn-ic-nbctl.at > +++ b/tests/ovn-ic-nbctl.at > @@ -63,3 +63,39 @@ AT_CHECK([ovn-ic-nbctl --if-exists ts-del ts2]) > > OVN_IC_NBCTL_TEST_STOP > AT_CLEANUP > + > +AT_SETUP([ovn-ic-nbctl]) > +OVN_IC_NBCTL_TEST_START > + > +AT_CHECK([ovn-ic-nbctl tr-add tr0]) > +AT_CHECK([ovn-ic-nbctl tr-list | uuidfilt], [0], [dnl > +<0> (tr0) > +]) > + > +AT_CHECK([ovn-ic-nbctl tr-add tr1]) > +AT_CHECK([ovn-ic-nbctl tr-list | uuidfilt], [0], [dnl > +<0> (tr0) > +<1> (tr1) > +]) > + > +AT_CHECK([ovn-ic-nbctl tr-del tr1]) > +AT_CHECK([ovn-ic-nbctl tr-list | uuidfilt], [0], [dnl > +<0> (tr0) > +]) > + > +AT_CHECK([ovn-ic-nbctl tr-add tr0], [1], [], > + [ovn-ic-nbctl: tr0: a transit router with this name already exists > +]) > + > +AT_CHECK([ovn-ic-nbctl --may-exist tr-add tr0]) > +AT_CHECK([ovn-ic-nbctl tr-list | uuidfilt], [0], [dnl > +<0> (tr0) > +]) > + > +AT_CHECK([ovn-ic-nbctl tr-del tr2], [1], [], > + [ovn-ic-nbctl: tr2: router name not found > +]) > + > +AT_CHECK([ovn-ic-nbctl --if-exists tr-del tr2]) > +OVN_IC_NBCTL_TEST_STOP > +AT_CLEANUP > diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at > index ced3fc9ed..3d36226f6 100644 > --- a/tests/ovn-ic.at > +++ b/tests/ovn-ic.at > @@ -4301,6 +4301,38 @@ ovn_as az2 check ovn-nbctl remove > logical_router_port lrp-lr12-ts1 options ic-ro > ovn_as az1 check ovn-nbctl remove logical_router_port lrp-lr11-ts1 > options ic-route-deny-learn > > > +OVN_CLEANUP_IC([az1], [az2]) > +AT_CLEANUP > +]) > + > +AT_BANNER([OVN Interconnection Controller Transit Router]) > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([ovn-ic -- Add transit router]) > + > +ovn_init_ic_db > +ovn_start az1 > +ovn_start az2 > + > +check ovn-ic-nbctl --wait=sb sync > +AT_CHECK([ovn-ic-sbctl show], [0], [dnl > +availability-zone az1 > +availability-zone az2 > +]) > + > +ovn_as az1 > +check ovn-ic-nbctl tr-add tr0 > +wait_row_count Datapath_Binding 1 > +az1_tunnel_key=$(ovn_as az1 ovn-sbctl --bare --columns=tunnel_key find > Datapath_Binding) > There is no check that the NB Logical Router has requested-tnl-key set. > + > +ovn_as az2 > +wait_row_count Datapath_Binding 1 > +az2_tunnel_key=$(ovn_as az2 ovn-sbctl --bare --columns=tunnel_key find > Datapath_Binding) > Same here. > + > +check test "${az1_tunnel_key}"=="${az2_tunnel_key}" > + > +check ovn-ic-nbctl tr-del tr0 > +wait_row_count Datapath_Binding 0 > + > OVN_CLEANUP_IC([az1], [az2]) > AT_CLEANUP > ]) > diff --git a/utilities/ovn-ic-nbctl.c b/utilities/ovn-ic-nbctl.c > index ed07e5902..6a94f863e 100644 > --- a/utilities/ovn-ic-nbctl.c > +++ b/utilities/ovn-ic-nbctl.c > @@ -337,6 +337,11 @@ Transit switch commands:\n\ > ts-del SWITCH delete SWITCH\n\ > ts-list print all transit switches\n\ > \n\ > +Transit router commands:\n\ > + tr-add ROUTER create a transit router named ROUTER\n\ > + tr-del ROUTER delete ROUTER\n\ > + tr-list print all transit routers\n\ > +\n\ > Connection commands:\n\ > get-connection print the connections\n\ > del-connection delete the connections\n\ > @@ -397,7 +402,17 @@ struct ic_nbctl_context { > static struct cmd_show_table cmd_show_tables[] = { > {&icnbrec_table_transit_switch, > &icnbrec_transit_switch_col_name, > - {NULL}, > + {NULL, NULL, NULL}, > + {NULL, NULL, NULL}}, > + > + {&icnbrec_table_transit_router, > + &icnbrec_transit_router_col_name, > + {NULL, NULL, NULL}, > + {NULL, NULL, NULL}}, > + > + {&icnbrec_table_transit_router_port, > + &icnbrec_transit_router_port_col_name, > + {&icnbrec_transit_router_port_col_tr_uuid, NULL, NULL}, > This should be part of the 4/4 patch. > {NULL, NULL, NULL}}, > > {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}}, > @@ -476,9 +491,8 @@ ic_nbctl_ts_del(struct ctl_context *ctx) > const char *id = ctx->argv[1]; > const struct icnbrec_transit_switch *ts = NULL; > > - char *error = ts_by_name_or_uuid(ctx, id, must_exist, &ts); > - if (error) { > - ctx->error = error; > + ctx->error = ts_by_name_or_uuid(ctx, id, must_exist, &ts); > + if (ctx->error) { > nit: Unrelated change. > return; > } > if (!ts) { > @@ -507,6 +521,106 @@ ic_nbctl_ts_list(struct ctl_context *ctx) > smap_destroy(&switches); > free(nodes); > } > + > +static char * > +tr_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool > must_exist, > + const struct icnbrec_transit_router **tr_p) > +{ > + const struct icnbrec_transit_router *tr = NULL; > + *tr_p = NULL; > + > + struct uuid tr_uuid; > + bool is_uuid = uuid_from_string(&tr_uuid, id); > + if (is_uuid) { > + tr = icnbrec_transit_router_get_for_uuid(ctx->idl, &tr_uuid); > + } > + > + if (!tr) { > + const struct icnbrec_transit_router *iter; > + > + ICNBREC_TRANSIT_ROUTER_FOR_EACH (iter, ctx->idl) { > + if (!strcmp(iter->name, id)) { > + tr = iter; > + break; > + } > + } > + } > + > + if (!tr && must_exist) { > + return xasprintf("%s: router %s not found", > + id, is_uuid ? "UUID" : "name"); > + } > + > + *tr_p = tr; > + return NULL; > +} > + > +static void > +ic_nbctl_tr_add(struct ctl_context *ctx) > +{ > + if (!ovsdb_idl_server_has_table(ctx->idl, > &icnbrec_table_transit_router)) { > + VLOG_WARN("icnbrec_table_transit_router missing"); > + } > + > + const char *tr_name = ctx->argv[1]; > + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; > + > + const struct icnbrec_transit_router *tr = NULL; > + ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->idl) { > + if (!strcmp(tr->name, tr_name)) { > + if (may_exist) { > + return; > + } > + ctl_error(ctx, "%s: a transit router with this name already " > + "exists", tr_name); > + return; > + } > + } > + > + tr = icnbrec_transit_router_insert(ctx->txn); > + icnbrec_transit_router_set_name(tr, tr_name); > +} > + > +static void > +ic_nbctl_tr_del(struct ctl_context *ctx) > +{ > + bool must_exist = !shash_find(&ctx->options, "--if-exists"); > + const char *id = ctx->argv[1]; > + const struct icnbrec_transit_router *tr = NULL; > + > + ctx->error = tr_by_name_or_uuid(ctx, id, must_exist, &tr); > + if (ctx->error) { > + return; > + } > + > + if (!tr) { > + return; > + } > + > + icnbrec_transit_router_delete(tr); > +} > + > +static void > +ic_nbctl_tr_list(struct ctl_context *ctx) > +{ > + const struct icnbrec_transit_router *tr; > + struct smap routers; > + > + smap_init(&routers); > + ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->idl) { > + smap_add_format(&routers, tr->name, UUID_FMT " (%s)", > + UUID_ARGS(&tr->header_.uuid), tr->name); > + } > + > + const struct smap_node **nodes = smap_sort(&routers); > + for (size_t i = 0; i < smap_count(&routers); i++) { > + const struct smap_node *node = nodes[i]; > + ds_put_format(&ctx->output, "%s\n", node->value); > + } > + > + smap_destroy(&routers); > + free(nodes); > +} > static void > verify_connections(struct ctl_context *ctx) > { > @@ -714,6 +828,9 @@ cmd_set_ssl(struct ctl_context *ctx) > static const struct ctl_table_class tables[ICNBREC_N_TABLES] = { > [ICNBREC_TABLE_TRANSIT_SWITCH].row_ids[0] = > {&icnbrec_transit_switch_col_name, NULL, NULL}, > + > + [ICNBREC_TABLE_TRANSIT_ROUTER].row_ids[0] = > + {&icnbrec_transit_router_col_name, NULL, NULL}, > }; > > > @@ -1015,6 +1132,13 @@ static const struct ctl_command_syntax > ic_nbctl_commands[] = { > { "ts-del", 1, 1, "SWITCH", NULL, ic_nbctl_ts_del, NULL, > "--if-exists", RW }, > { "ts-list", 0, 0, "", NULL, ic_nbctl_ts_list, NULL, "", RO }, > > + /* transit router commands. */ > + { "tr-add", 1, 1, "ROUTER", NULL, ic_nbctl_tr_add, NULL, > "--may-exist", > + RW }, > + { "tr-del", 1, 1, "ROUTER", NULL, ic_nbctl_tr_del, NULL, > "--if-exists", > + RW }, > + { "tr-list", 0, 0, "", NULL, ic_nbctl_tr_list, NULL, "", RO }, > + > /* Connection commands. */ > {"get-connection", 0, 0, "", pre_connection, cmd_get_connection, > NULL, "", > RO}, > -- > 2.51.0 > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > Thanks, Ales _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
