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

Reply via email to