On Wed, Oct 01, 2025 at 12:17:30PM +0200, Ales Musil wrote:
> On Wed, Oct 1, 2025 at 12:56 AM Mairtin O'Loingsigh via dev <
> [email protected]> wrote:
> 
> > This patch adds support for adding transit routers and transit router
> > ports. When created, a transit router creates logical routers in each AZ
> > with the same global datapath binding.
> >
> > Ports create on a transit router with be duplicated across all AZs. If
> > the port has a requested chassis, all ports except the port in the
> > chassis' AZ will be remote.
> >
> > 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
> > Commands to add and delete a transit router port.
> >     $ ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
> >         192.168.10.20/24 chassis=ovn-chassis-1
> >     $ ovn-ic-nbctl trp-del tr0-p0
> >
> > Reported-at: https://issues.redhat.com/browse/FDP-1476
> > Reported-at: https://issues.redhat.com/browse/FDP-1477
> > Reported-at: https://issues.redhat.com/browse/FDP-1478
> > Signed-off-by: Mairtin O'Loingsigh <[email protected]>
> > ---
> >
> 
> Hi Mairtin,
> 
> thank you for the patch, the fact that it had to be squashed into single
> commit
> tells that there is something wrong with the schema. Also this
> change is pretty big on it's own, I believe that this could be split even
> further
> into:
> 1) Schema change
> 2) tr changes (nbctl + ovn-ic)
> 3) trp changes (nbctl + ovn-ic)
> 
> I left some comments down below.
Ack, Ill split the patch
> 
> 
> >  NEWS                     |    6 +
> >  ic/ovn-ic.c              | 1012 +++++++++++++++++++++++++++++++-------
> >  ovn-architecture.7.xml   |   11 +-
> >  ovn-ic-nb.ovsschema      |   32 +-
> >  ovn-ic-nb.xml            |   70 +++
> >  ovn-ic-sb.ovsschema      |   31 +-
> >  ovn-ic-sb.xml            |   28 ++
> >  tests/ovn-ic-nbctl.at    |   41 ++
> >  tests/ovn-ic-sbctl.at    |    2 +-
> >  tests/ovn-ic.at          |  201 +++++++-
> >  utilities/ovn-ic-nbctl.c |  266 +++++++++-
> >  11 files changed, 1519 insertions(+), 181 deletions(-)
> >
> > diff --git a/NEWS b/NEWS
> > index f9ad8ae75..e97817e05 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -15,6 +15,12 @@ Post v25.09.0
> >         (Static_)MAC_Bindings of adjacent logical routers over ARPs learned
> >         through EVPN on the switch.
> >
> > +   - Added Transit Router support:
> > +    * Support the creation of Transit Routers.
> > +    * Support the creation of Transit Router Ports with replication
> > +        across availability zones.
> > +    * Added new ovn-ic-nbctl
> > 'tr-add','tr-del','tr-list''trp-add','trp-del'
> > +        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 cea64588b..28ac43600 100644
> > --- a/ic/ovn-ic.c
> > +++ b/ic/ovn-ic.c
> > @@ -66,6 +66,7 @@ struct ic_context {
> >      struct ovsdb_idl_txn *ovnisb_txn;
> >      const struct icsbrec_availability_zone *runned_az;
> >      struct ovsdb_idl_index *nbrec_ls_by_name;
> > +    struct ovsdb_idl_index *nbrec_lr_by_name;
> >      struct ovsdb_idl_index *nbrec_lrp_by_name;
> >      struct ovsdb_idl_index *nbrec_port_by_name;
> >      struct ovsdb_idl_index *sbrec_chassis_by_name;
> > @@ -75,14 +76,14 @@ struct ic_context {
> >      struct ovsdb_idl_index
> > *sbrec_service_monitor_by_remote_type_logical_port;
> >      struct ovsdb_idl_index *icnbrec_transit_switch_by_name;
> >      struct ovsdb_idl_index *icsbrec_port_binding_by_az;
> > -    struct ovsdb_idl_index *icsbrec_port_binding_by_ts;
> > -    struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az;
> > +    struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid;
> > +    struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid_type;
> >      struct ovsdb_idl_index *icsbrec_route_by_az;
> > -    struct ovsdb_idl_index *icsbrec_route_by_ts;
> > -    struct ovsdb_idl_index *icsbrec_route_by_ts_az;
> >      struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az;
> >      struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az;
> >      struct ovsdb_idl_index
> > *icsbrec_service_monitor_by_target_az_logical_port;
> > +    struct ovsdb_idl_index *icsbrec_route_by_nb_ic_uuid_type;
> > +    struct ovsdb_idl_index *icsbrec_route_by_nb_ic_az;
> >  };
> >
> >  struct ic_state {
> > @@ -190,31 +191,78 @@ az_run(struct ic_context *ctx)
> >          ctx->runned_az = az;
> >          return az;
> >      }
> > +
> >
> 
> nit: Unrelated change.

Ack, fine
> 
> 
> >      return NULL;
> >  }
> >
> >  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;
> > -    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);
> >  }
> >
> > +
> > +struct ic_dp_info {
> > +    struct hmap_node node;
> > +    struct uuid uuid;
> > +    const struct icsbrec_datapath_binding *isb_dp;
> > +};
> > +
> > +static struct ic_dp_info *
> > +ic_dp_find(struct hmap *dp_info, const struct uuid *uuid)
> > +{
> > +    struct ic_dp_info *info;
> > +    HMAP_FOR_EACH_WITH_HASH (info, node, uuid_hash(uuid),
> > +                             dp_info) {
> > +        if (uuid_equals(&info->uuid, uuid)) {
> > +            return info;
> > +        }
> > +
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static const struct icsbrec_datapath_binding *
> > +ic_dp_find_and_remove(struct hmap *dp_info, const struct uuid *uuid)
> > +{
> > +    struct ic_dp_info *info = ic_dp_find(dp_info, uuid);
> > +    if (info) {
> > +        hmap_remove(dp_info, &info->node);
> > +        const struct icsbrec_datapath_binding *isb_dp = info->isb_dp;
> > +        free(info);
> > +        return isb_dp;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> >  static void
> > -ts_run(struct ic_context *ctx)
> > +ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,
> > +    struct hmap *isb_ts_dps, struct hmap *isb_tr_dps)
> >
> 
> nit: Unaligned arguments. Please check the other
> function definitions and calls.
> 
Ack, ill fix
> 
> >  {
> >      const struct icnbrec_transit_switch *ts;
> >      bool dp_key_refresh = false;
> >
> > -    struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
> > -    struct shash isb_dps = SHASH_INITIALIZER(&isb_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);
> > +
> > +        struct ic_dp_info *dp_info;
> > +        dp_info = xzalloc(sizeof *dp_info);
> >
> 
> There doesn't seem to be a reason to do zero-alloc.
> Also it would be nice to use a designated initializer.
Ack, Ill change to designate initializer.
> 
> 
> > +        dp_info->isb_dp = isb_dp;
> > +        dp_info->uuid = isb_dp->nb_ic_uuid;
> > +        if (isb_dp->type && !strcmp(isb_dp->type, "transit-router")) {
> >
> 
> It actually might be beneficial to have an enum that and
> helper function that will return the enum type.
Ack, ill add a helper for the enum return type
> 
> +            hmap_insert(isb_tr_dps, &dp_info->node, uuid_hash(
> > +                &isb_dp->nb_ic_uuid));
> > +            continue;
> > +        }
> > +
> > +        hmap_insert(isb_ts_dps, &dp_info->node, uuid_hash(
> > +            &isb_dp->nb_ic_uuid));
> >      }
> >
> >      bool vxlan_mode = false;
> > @@ -230,12 +278,10 @@ ts_run(struct ic_context *ctx)
> >              }
> >          }
> >      }
> > -
> >
> 
> nit: Unrelated change.
Ack,
> 
> 
> >      /* Sync INB TS to AZ NB */
> >      if (ctx->ovnnb_txn) {
> >          struct shash nb_tses = SHASH_INITIALIZER(&nb_tses);
> >          const struct nbrec_logical_switch *ls;
> > -
> >
> 
> nit: Unrelated change.
Ack, ill remove
> 
> 
> >          /* Get current NB Logical_Switch with other_config:interconn-ts */
> >          NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->ovnnb_idl) {
> >              const char *ts_name = smap_get(&ls->other_config,
> > "interconn-ts");
> > @@ -266,17 +312,18 @@ ts_run(struct ic_context *ctx)
> >                  }
> >              }
> >
> > -            isb_dp = shash_find_data(&isb_dps, ts->name);
> > -            if (isb_dp) {
> > +            struct ic_dp_info *dp_info = ic_dp_find(isb_ts_dps,
> > +                &ts->header_.uuid);
> > +            if (dp_info) {
> >                  int64_t nb_tnl_key = smap_get_int(&ls->other_config,
> >                                                    "requested-tnl-key",
> >                                                    0);
> > -                if (nb_tnl_key != isb_dp->tunnel_key) {
> > +                if (nb_tnl_key != dp_info->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);
> > +                             dp_info->isb_dp->tunnel_key, ls->name);
> >                      char *tnl_key_str = xasprintf("%"PRId64,
> > -                                                  isb_dp->tunnel_key);
> > +
> > dp_info->isb_dp->tunnel_key);
> >                      nbrec_logical_switch_update_other_config_setkey(
> >                          ls, "requested-tnl-key", tnl_key_str);
> >                      free(tnl_key_str);
> > @@ -298,10 +345,11 @@ 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);
> > +            isb_dp = ic_dp_find_and_remove(isb_ts_dps, &ts->header_.uuid);
> >              if (!isb_dp) {
> >                  /* Allocate tunnel key */
> > -                int64_t dp_key = allocate_ts_dp_key(&dp_tnlids,
> > vxlan_mode);
> > +                int64_t dp_key = allocate_dp_key(dp_tnlids, vxlan_mode,
> > +                    "transit switch datapath");
> >
>                  if (!dp_key) {
> >                      continue;
> >                  }
> > @@ -309,23 +357,148 @@ 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);
> > +                icsbrec_datapath_binding_set_type(isb_dp,
> > "transit-switch");
> > +                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);
> >                  }
> > +
> >              }
> >          }
> >
> > -        /* Delete extra ISB Datapath_Binding */
> > +        struct ic_dp_info *dp_info;
> > +        HMAP_FOR_EACH_POP (dp_info, node, isb_ts_dps) {
> > +            icsbrec_datapath_binding_delete(dp_info->isb_dp);
> > +            free(dp_info);
> > +        }
> > +    }
> > +}
> > +
> > +static const struct sbrec_chassis *
> > +find_sb_chassis(struct ic_context *ctx, const char *name)
> > +{
> > +    const struct sbrec_chassis *key =
> > +        sbrec_chassis_index_init_row(ctx->sbrec_chassis_by_name);
> > +    sbrec_chassis_index_set_name(key, name);
> > +
> > +    const struct sbrec_chassis *chassis =
> > +        sbrec_chassis_index_find(ctx->sbrec_chassis_by_name, key);
> > +    sbrec_chassis_index_destroy_row(key);
> > +
> > +    return chassis;
> > +}
> > +
> > +static void
> > +tr_run(struct ic_context *ctx, struct hmap *dp_tnlids,
> > +    struct hmap *isb_tr_dps)
> > +{
> > +    struct shash nb_tres = SHASH_INITIALIZER(&nb_tres);
> > +    const struct nbrec_logical_router *lr;
> > +
> > +    if (ctx->ovnnb_txn) {
> > +        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);
> > +            }
> > +
> >
> 
> nit: Extra empty line. Please check the rest of the file too.
Ack, remove extra line
> 
> +        }
> > +
> > +        const struct icnbrec_transit_router *tr;
> > +        ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->ovninb_idl) {
> > +            lr = shash_find_data(&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);
> > +                shash_add(&nb_tres, tr->name, lr);
> > +            }
> > +
> > +            struct ic_dp_info *dp_info = ic_dp_find(isb_tr_dps,
> > +                &tr->header_.uuid);
> > +            if (dp_info) {
> > +                int64_t nb_tnl_key = smap_get_int(&lr->options,
> > +                                                  "requested-tnl-key",
> > +                                                  0);
> > +                if (nb_tnl_key != dp_info->isb_dp->tunnel_key) {
> > +                    VLOG_DBG("Set other_config:requested-tnl-key %"PRId64
> > +                             " for transit router %s in NB.",
> > +                             dp_info->isb_dp->tunnel_key, lr->name);
> > +                    char *tnl_key_str = xasprintf("%"PRId64,
> > +
> > dp_info->isb_dp->tunnel_key);
> > +                    nbrec_logical_router_update_options_setkey(
> > +                        lr, "requested-tnl-key", tnl_key_str);
> > +                    free(tnl_key_str);
> > +                }
> > +
> > +            }
> > +        }
> > +    }
> > +
> > +    /* Sync TS 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) {
> > +            lr = shash_find_and_delete(&nb_tres, tr->name);
> > +            struct ic_dp_info *dp_info = ic_dp_find(isb_tr_dps,
> > +                &tr->header_.uuid);
> > +            if (!dp_info) {
> > +                int dp_key = smap_get_int(&tr->other_config,
> > +                    "requested-tnl-key", 0);
> > +                if (!dp_key) {
> > +                    dp_key = allocate_dp_key(dp_tnlids, false,
> > +                        "transit router datapath");
> > +                    if (!dp_key) {
> > +                        VLOG_DBG("Set options:requested-tnl-key %"PRId32
> > +                            " for lr %s in ISB.", dp_key, tr->name);
> > +                        continue;
> > +                    }
> > +
> > +                }
> > +
> > +                struct icsbrec_datapath_binding *isb_dp;
> > +                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);
> > +                icsbrec_datapath_binding_set_type(isb_dp,
> > "transit-router");
> > +            } else {
> > +                hmap_remove(isb_tr_dps, &dp_info->node);
> > +                free(dp_info);
> > +            }
> > +
> > +        }
> > +        struct ic_dp_info *dp_info;
> > +        HMAP_FOR_EACH_POP (dp_info, node, isb_tr_dps) {
> > +            icsbrec_datapath_binding_delete(dp_info->isb_dp);
> > +            free(dp_info);
> > +        }
> >          struct shash_node *node;
> > -        SHASH_FOR_EACH (node, &isb_dps) {
> > -            icsbrec_datapath_binding_delete(node->data);
> > +        SHASH_FOR_EACH (node, &nb_tres) {
> > +            lr = node->data;
> > +            nbrec_logical_router_delete(lr);
> > +            const struct icnbrec_transit_router_port *trp;
> > +            ICNBREC_TRANSIT_ROUTER_PORT_FOR_EACH (trp, ctx->ovninb_idl) {
> > +                if (!strcmp(trp->transit_router, lr->name)) {
> > +                    icnbrec_transit_router_port_delete(trp);
> > +                }
> > +
> > +            }
> >          }
> >      }
> > -    ovn_destroy_tnlids(&dp_tnlids);
> > -    shash_destroy(&isb_dps);
> > +    shash_destroy(&nb_tres);
> >  }
> >
> >  /* Returns true if any information in gw and chassis is different. */
> > @@ -451,6 +624,7 @@ gateway_run(struct ic_context *ctx)
> >              gw = shash_find_and_delete(&remote_gws, chassis->name);
> >              if (!gw) {
> >                  sbrec_chassis_delete(chassis);
> > +                continue;
> >
> 
> This continue doesn't do anything for the loop outcome.
Ack, continue removed
> 
> 
> >              } else if (is_gateway_data_changed(gw, chassis)) {
> >                  sync_isb_gw_to_sb(ctx, gw, chassis);
> >              }
> > @@ -498,6 +672,31 @@ find_ts_in_nb(struct ic_context *ctx, char *ts_name)
> >      return NULL;
> >  }
> >
> > +static const struct nbrec_logical_router*
> > +find_tr_in_nb(struct ic_context *ctx, char *tr_name)
> > +{
> > +    const struct nbrec_logical_router *key =
> > +        nbrec_logical_router_index_init_row(ctx->nbrec_lr_by_name);
> > +    nbrec_logical_router_index_set_name(key, tr_name);
> > +
> > +    const struct nbrec_logical_router *lr;
> > +    bool found = false;
> > +    NBREC_LOGICAL_ROUTER_FOR_EACH_EQUAL (lr, key, ctx->nbrec_lr_by_name) {
> > +        if (smap_get(&lr->options, "interconn-tr")) {
> > +            found = true;
> > +            break;
> > +        }
> > +
> > +    }
> > +    nbrec_logical_router_index_destroy_row(key);
> > +
> > +    if (found) {
> > +        return lr;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> >  static const struct sbrec_port_binding *
> >  find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> >                     const char *name)
> > @@ -584,20 +783,6 @@ get_lp_address_for_sb_pb(struct ic_context *ctx,
> >      return peer->n_mac ? *peer->mac : NULL;
> >  }
> >
> > -static const struct sbrec_chassis *
> > -find_sb_chassis(struct ic_context *ctx, const char *name)
> > -{
> > -    const struct sbrec_chassis *key =
> > -        sbrec_chassis_index_init_row(ctx->sbrec_chassis_by_name);
> > -    sbrec_chassis_index_set_name(key, name);
> > -
> > -    const struct sbrec_chassis *chassis =
> > -        sbrec_chassis_index_find(ctx->sbrec_chassis_by_name, key);
> > -    sbrec_chassis_index_destroy_row(key);
> > -
> > -    return chassis;
> > -}
> > -
> >  static void
> >  sync_lsp_tnl_key(const struct nbrec_logical_switch_port *lsp,
> >                   int64_t isb_tnl_key)
> > @@ -615,6 +800,23 @@ sync_lsp_tnl_key(const struct
> > nbrec_logical_switch_port *lsp,
> >
> >  }
> >
> > +static void
> > +sync_lrp_tnl_key(const struct nbrec_logical_router_port *lrp,
> > +                 int64_t isb_tnl_key)
> > +{
> > +    int64_t tnl_key = smap_get_int(&lrp->options, "requested-tnl-key", 0);
> > +    if (tnl_key != isb_tnl_key) {
> > +        VLOG_DBG("Set options:requested-tnl-key %"PRId64
> > +                 " for lrp %s in NB.", isb_tnl_key, lrp->name);
> > +        char *tnl_key_str = xasprintf("%"PRId64, isb_tnl_key);
> > +        nbrec_logical_router_port_update_options_setkey(lrp,
> > +
> > "requested-tnl-key",
> > +                                                        tnl_key_str);
> > +        free(tnl_key_str);
> > +    }
> > +
> > +}
> > +
> >  static bool
> >  get_router_uuid_by_sb_pb(struct ic_context *ctx,
> >                           const struct sbrec_port_binding *sb_pb,
> > @@ -701,6 +903,49 @@ sync_local_port(struct ic_context *ctx,
> >      sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);
> >  }
> >
> > +struct ic_trp_data {
> > +    const struct icnbrec_transit_router_port *trp;
> > +    char *name;
> > +    char *tr_name;
> > +    char *mac;
> > +    char *chassis;
> > +    struct uuid nb_ic_uuid;
> > +    const struct nbrec_logical_router_port *lrp;
> > +};
> > +
> > +/* For each local port:
> > + *   - Sync from NB to ISB.
> > + *   - Sync gateway from SB to ISB.
> > + *   - Sync tunnel key from ISB to NB.
> > + */
> > +static void
> > +sync_local_router_port(struct ic_context *ctx,
> > +                const struct icsbrec_port_binding *isb_pb,
> > +                const struct sbrec_port_binding *sb_pb,
> > +                struct ic_trp_data *trp_data)
> > +{
> > +    (void) ctx;
> > +    (void) sb_pb;
> >
> 
> Please don't pass unused arguments.
> Also this function is a bit backwards isn't it?
> We are supposed to sync those thing from IC databases
> into NB.
Ack, Yes, both should sync from IC, ill change this.
> 
> 
> > +    const struct nbrec_logical_router_port *lrp = trp_data->lrp;
> > +
> > +    /* Sync address from NB to ISB */
> > +    if (!strcmp(lrp->mac, isb_pb->address)) {
> > +        icsbrec_port_binding_set_address(isb_pb, lrp->mac);
> > +    }
> > +
> > +    const char *chassis_name = smap_get(&lrp->options,
> > "requested-chassis");
> > +    if (chassis_name) {
> > +        if (!strcmp(chassis_name, trp_data->chassis)) {
> > +            icnbrec_transit_router_port_set_chassis(trp_data->trp,
> > +                chassis_name);
> > +        }
> > +
> > +    }
> > +
> > +    /* Sync back tunnel key from ISB to NB */
> > +    sync_lrp_tnl_key(lrp, isb_pb->tunnel_key);
> > +}
> > +
> >  /* For each remote port:
> >   *   - Sync from ISB to NB
> >   *   - Sync gateway from ISB to SB
> > @@ -752,6 +997,32 @@ sync_remote_port(struct ic_context *ctx,
> >      }
> >  }
> >
> > +/* For each remote port:
> > + *   - Sync from ISB to NB
> > + */
> > +static void
> > +sync_remote_router_port(struct ic_context *ctx OVS_UNUSED,
> > +                 const struct icsbrec_port_binding *isb_pb,
> > +                 const struct ic_trp_data *trp_data,
> > +                 const struct sbrec_port_binding *sb_pb OVS_UNUSED)
> > +{
> >
> 
> The sync should actually be same for both
> local and remote TRPs. Only exception is the
> the "requested-chassis" option, remote should
> set it, local should clear it.
> 
Ack, ill update syncs to match, unless requested-chassis is set
> 
> > +    const struct nbrec_logical_router_port *lrp = trp_data->lrp;
> > +
> > +    /* Sync from ICNB to NB */
> > +    if (strcmp(trp_data->chassis, "")) {
> >
> 
> nit: "!trp_data->chassis[0]"
Ack, change to above
> 
> +        const char *chassis_name = smap_get(&lrp->options,
> > +            "requested-chassis");
> > +        if (!chassis_name || strcmp(trp_data->chassis, chassis_name)) {
> > +            nbrec_logical_router_port_update_options_setkey(lrp,
> > +                "requested-chassis", trp_data->chassis);
> > +        }
> > +
> > +    }
> > +
> > +    /* Sync tunnel key from ISB to NB */
> > +    sync_lrp_tnl_key(lrp, isb_pb->tunnel_key);
> > +}
> > +
> >  static void
> >  create_nb_lsp(struct ic_context *ctx,
> >                const struct icsbrec_port_binding *isb_pb,
> > @@ -778,6 +1049,9 @@ create_isb_pb(struct ic_context *ctx,
> >                const struct sbrec_port_binding *sb_pb,
> >                const struct icsbrec_availability_zone *az,
> >                const char *ts_name,
> > +              const struct uuid *nb_ic_uuid,
> > +              const char *type,
> > +              const char *mac,
> >                uint32_t pb_tnl_key)
> >  {
> >      const struct icsbrec_port_binding *isb_pb =
> > @@ -786,10 +1060,17 @@ create_isb_pb(struct ic_context *ctx,
> >      icsbrec_port_binding_set_transit_switch(isb_pb, ts_name);
> >      icsbrec_port_binding_set_logical_port(isb_pb, sb_pb->logical_port);
> >      icsbrec_port_binding_set_tunnel_key(isb_pb, pb_tnl_key);
> > +    icsbrec_port_binding_set_nb_ic_uuid(isb_pb, *nb_ic_uuid);
> > +    icsbrec_port_binding_set_type(isb_pb, type);
> >
> > -    const char *address = get_lp_address_for_sb_pb(ctx, sb_pb);
> > -    if (address) {
> > -        icsbrec_port_binding_set_address(isb_pb, address);
> > +    if (!strcmp(type, "transit-switch")) {
> > +        const char *address = get_lp_address_for_sb_pb(ctx, sb_pb);
> > +        if (address) {
> > +            icsbrec_port_binding_set_address(isb_pb, address);
> > +        }
> > +    } else {
> > +        icsbrec_port_binding_set_address(isb_pb, mac);
> > +        return;
> >      }
> >
> >      const struct sbrec_port_binding *crp = find_crp_for_sb_pb(ctx, sb_pb);
> > @@ -808,10 +1089,9 @@ create_isb_pb(struct ic_context *ctx,
> >  }
> >
> >  static const struct sbrec_port_binding *
> > -find_lsp_in_sb(struct ic_context *ctx,
> > -               const struct nbrec_logical_switch_port *lsp)
> > +find_lp_in_sb(struct ic_context *ctx, const char *name)
> >  {
> > -    return find_sb_pb_by_name(ctx->sbrec_port_binding_by_name, lsp->name);
> > +    return find_sb_pb_by_name(ctx->sbrec_port_binding_by_name, name);
> >  }
> >
> >  static uint32_t
> > @@ -822,6 +1102,236 @@ allocate_port_key(struct hmap *pb_tnlids)
> >                                1, (1u << 15) - 1, &hint);
> >  }
> >
> > +struct ic_pb_info {
> > +    struct hmap_node node;
> > +    struct uuid uuid;
> > +    const struct icsbrec_port_binding *isb_pb;
> > +    char *logical_port;
> > +};
> > +
> > +static struct ic_pb_info *
> > +ic_pb_create(const struct icsbrec_port_binding *isb_pb)
> > +{
> > +    struct ic_pb_info *info = xzalloc(sizeof *info);
> >
> > No need for zero alloc and please use designated initializer.
Ack, ill change to designated initializer
> 
> 
> > +    info->isb_pb = isb_pb;
> > +    info->uuid = isb_pb->nb_ic_uuid;
> > +    info->logical_port = xstrdup(isb_pb->logical_port);
> > +    return info;
> > +}
> > +
> > +static void
> > +ic_pb_insert(struct hmap *dp_info, struct ic_pb_info *info)
> > +{
> > +    if (info) {
> >
> 
> This check can be removed, there is always a non-NULL value passed.
Ack
> 
> 
> > +        size_t hash = uuid_hash(&info->isb_pb->nb_ic_uuid);
> > +        hash = hash_string(info->isb_pb->logical_port, hash);
> > +        hmap_insert(dp_info, &info->node, hash);
> > +    }
> > +
> > +}
> > +
> > +static struct ic_pb_info *
> > +ic_pb_find(struct hmap *dp_info, const struct uuid *nb_ic_uuid,
> > +    const char *logical_port)
> > +{
> > +    size_t hash = uuid_hash(nb_ic_uuid);
> > +    hash = hash_string(logical_port, hash);
> >
> 
> It's usually a good idea to have a separate hash function
> when there are more things to hash.
Ack, ill add a hash function
> 
> +
> > +    struct ic_pb_info *info;
> > +    HMAP_FOR_EACH_WITH_HASH (info, node, hash, dp_info) {
> > +        if (uuid_equals(&info->uuid, nb_ic_uuid) &&
> > +                !strcmp(info->logical_port, logical_port)) {
> > +           return info;
> > +        }
> > +
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static const struct icsbrec_port_binding *
> > +ic_pb_find_and_remove(struct hmap *dp_info, const struct uuid *nb_ic_uuid,
> > +    const char *logical_port)
> > +{
> > +    struct ic_pb_info *info = ic_pb_find(dp_info, nb_ic_uuid,
> > logical_port);
> > +    if (info) {
> > +        hmap_remove(dp_info, &info->node);
> > +        const struct icsbrec_port_binding *isb_pb = info->isb_pb;
> > +        free(info->logical_port);
> > +        free(info);
> > +        return isb_pb;
> > +    }
> > +
> > +    return NULL;
> > +}
> 
> +
> > +static struct ic_pb_info *
> > +ic_pb_remove(struct hmap *dp_info, const struct uuid *nb_ic_uuid,
> > +    const char *logical_port)
> > +{
> > +    size_t hash = uuid_hash(nb_ic_uuid);
> > +    hash = hash_string(logical_port, hash);
> > +    struct ic_pb_info *info;
> > +    HMAP_FOR_EACH_WITH_HASH (info, node, hash, dp_info) {
> > +        if (uuid_equals(&info->uuid, nb_ic_uuid) &&
> > +                !strcmp(info->logical_port, logical_port)) {
> > +
> > +            hmap_remove(dp_info, &info->node);
> > +            return info;
> > +        }
> > +
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static const struct icsbrec_port_binding *
> > +ic_pb_free(struct ic_pb_info *pb_info)
> > +{
> > +    if (pb_info) {
> > +        const struct icsbrec_port_binding *isb_pb = pb_info->isb_pb;
> > +        free(pb_info->logical_port);
> > +        free(pb_info);
> > +        return isb_pb;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +static const struct icsbrec_port_binding *
> > +ic_pb_remove_node_and_free(struct hmap *dp_info, struct ic_pb_info
> > *pb_info)
> > +{
> > +    if (pb_info) {
> > +        hmap_remove(dp_info, &pb_info->node);
> > +        return ic_pb_free(pb_info);
> > +    }
> > +
> > +    return NULL;
> > +}
> >
> 
> This feels like unnecessary amount of functions,
> we usually define the following for structs like these:
> *_add()
> *_find()
> *_destroy()
> *_hash()
> 
> most of the logic is representable by the four above.
Ack, ill change to the above functions
> 
> 
> > +
> > +
> > +static const struct nbrec_logical_router_port *
> > +get_lrp_by_lrp_name(struct ic_context *ctx, const char *lrp_name)
> > +{
> > +    const struct nbrec_logical_router_port *lrp;
> > +    const struct nbrec_logical_router_port *lrp_key =
> > +        nbrec_logical_router_port_index_init_row(ctx->nbrec_lrp_by_name);
> > +    nbrec_logical_router_port_index_set_name(lrp_key, lrp_name);
> > +    lrp = nbrec_logical_router_port_index_find(ctx->nbrec_lrp_by_name,
> > +                                               lrp_key);
> > +    nbrec_logical_router_port_index_destroy_row(lrp_key);
> > +
> > +    return lrp;
> > +}
> > +
> > +static void
> > +trp_parse(struct ic_context *ctx,
> > +                 struct shash *trp_ports)
> > +{
> > +    const struct icnbrec_transit_router_port *trp;
> > +    ICNBREC_TRANSIT_ROUTER_PORT_FOR_EACH (trp, ctx->ovninb_idl) {
> > +        const struct nbrec_logical_router *lr = find_tr_in_nb(ctx,
> > +            trp->transit_router);
> > +        if (!lr) {
> > +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > +            VLOG_WARN_RL(&rl, "Transit Router Port %s or lr: %s not
> > found.",
> > +                trp->name, trp->transit_router);
> > +            continue;
> > +        }
> > +
> > +        const struct nbrec_logical_router_port *lrp =
> > +            get_lrp_by_lrp_name(ctx, trp->name);
> > +        if (!lrp) {
> >
> +            lrp = nbrec_logical_router_port_insert(ctx->ovnnb_txn);
> > +            nbrec_logical_router_port_set_name(lrp, trp->name);
> > +            nbrec_logical_router_port_set_mac(lrp, trp->mac);
> > +            if (strcmp("", trp->chassis)) {
> > +                nbrec_logical_router_port_update_options_setkey(lrp,
> > +                    "requested-chassis", trp->chassis);
> > +            }
> > +
> > +            nbrec_logical_router_port_set_networks(lrp,
> > +                (const char **) trp->networks, trp->n_networks);
> > +
> > +            nbrec_logical_router_port_update_options_setkey(lrp,
> > +                "interconn-tr", lr->name);
> > +
> > +            nbrec_logical_router_update_ports_addvalue(lr, lrp);
> > +        }
> > +
> > +        struct ic_trp_data *trp_data = shash_find_data(trp_ports,
> > +            trp->name);
> > +        if (!trp_data) {
> > +            trp_data = xzalloc(sizeof *trp_data);
> > +            trp_data->mac = xstrdup(trp->mac);
> > +            trp_data->name = xstrdup(trp->name);
> > +            trp_data->chassis = xstrdup(trp->chassis);
> > +            trp_data->tr_name = xstrdup(trp->transit_router);
> > +            trp_data->nb_ic_uuid = trp->nb_ic_uuid;
> > +            trp_data->lrp = lrp;
> > +            trp_data->trp = trp;
> > +            shash_add(trp_ports, trp->name, trp_data);
> > +        }
> > +
> > +    }
> > +}
> > +
> > +static bool
> > +trp_port_is_remote(struct ic_context *ctx,
> > +                const struct nbrec_logical_router_port *lrp) {
> > +
> > +    const char *chassis_name = smap_get(&lrp->options,
> > "requested-chassis");
> > +    if (chassis_name) {
> > +        const struct sbrec_chassis *chassis = find_sb_chassis(ctx,
> > +            chassis_name);
> > +        if (chassis) {
> > +            return smap_get_bool(&chassis->other_config, "is-remote",
> > false);
> > +        }
> > +
> > +    }
> > +
> > +    return false;
> > +}
> > +
> > +static void
> > +trp_port_sync(struct ic_context *ctx,
> > +            struct ic_trp_data *trp_data,
> > +            const struct icsbrec_availability_zone *az,
> > +            struct hmap *local_pbs, struct hmap *remote_pbs,
> > +            struct hmap *pb_tnlids,
> > +            const struct sbrec_port_binding *sb_pb)
> > +{
> >
>
I did not include non remote ports in the IC SB because they can have
different tunnel keys in different AZs, and I thought that would flood
the IC SB. Also, I saw the IC SB as a way to advertise information that
should be consistent across all AZ, since the keys change per AZ I left them
out.
> I'm little puzzled by the logic, why should only
> port with remote chassis in IC SB? Both of types in fact
> should be in IC SB.
> 
> +
> > +    const struct icsbrec_port_binding *isb_pb;
> > +    bool is_remote_port = trp_port_is_remote(ctx, trp_data->lrp);
> > +    if (!is_remote_port) {
> > +        /* Only port with a remote chassis get an entry in ICSB */
> > +        if (!!find_sb_chassis(ctx, trp_data->chassis)) {
> >
> 
> nit: The double !! is not needed.
Ack, ill remove the double !!
> 
> 
> > +            isb_pb = ic_pb_find_and_remove(local_pbs,
> > &trp_data->nb_ic_uuid,
> > +                trp_data->name);
> > +            if (!isb_pb) {
> > +                uint32_t pb_tnl_key = allocate_port_key(pb_tnlids);
> > +                create_isb_pb(ctx, sb_pb, az, trp_data->name,
> > +                    &trp_data->nb_ic_uuid,
> > +                    "transit-router", trp_data->mac, pb_tnl_key);
> > +            } else {
> > +                sync_local_router_port(ctx, isb_pb, sb_pb, trp_data);
> > +            }
> > +        }
> > +
> > +    } else {
> > +        isb_pb = ic_pb_find_and_remove(remote_pbs, &trp_data->nb_ic_uuid,
> > +            trp_data->name);
> > +       if (isb_pb) {
> > +           sb_pb = find_lp_in_sb(ctx, trp_data->name);
> > +            if (!sb_pb) {
> > +                return;
> > +            }
> > +
> > +            sync_remote_router_port(ctx, isb_pb, trp_data, sb_pb);
> > +       }
> > +
> > +    }
> > +}
> > +
> >  static void
> >  port_binding_run(struct ic_context *ctx)
> >  {
> > @@ -829,46 +1339,54 @@ port_binding_run(struct ic_context *ctx)
> >          return;
> >      }
> >
> > -    struct shash isb_all_local_pbs =
> > SHASH_INITIALIZER(&isb_all_local_pbs);
> > -    struct shash_node *node;
> > -
> > -    const struct icsbrec_port_binding *isb_pb;
> > -    const struct icsbrec_port_binding *isb_pb_key =
> > -
> > icsbrec_port_binding_index_init_row(ctx->icsbrec_port_binding_by_az);
> > -    icsbrec_port_binding_index_set_availability_zone(isb_pb_key,
> > -                                                     ctx->runned_az);
> > +    struct hmap isb_all_local_pbs = HMAP_INITIALIZER(&isb_all_local_pbs);
> > +    const struct icsbrec_port_binding *current_isb_pb;
> > +    struct hmap pb_tnlids = HMAP_INITIALIZER(&pb_tnlids);
> > +    ICSBREC_PORT_BINDING_FOR_EACH (current_isb_pb, ctx->ovnisb_idl) {
> > +        if (!strcmp(ctx->runned_az->name,
> > +                current_isb_pb->availability_zone->name)) {
> > +            struct ic_pb_info *pb_info = ic_pb_create(current_isb_pb);
> > +            ic_pb_insert(&isb_all_local_pbs, pb_info);
> > +        }
> >
> > -    ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
> > -                                         ctx->icsbrec_port_binding_by_az)
> > {
> > -        shash_add(&isb_all_local_pbs, isb_pb->logical_port, isb_pb);
> > +        ovn_add_tnlid(&pb_tnlids, current_isb_pb->tunnel_key);
> >      }
> > -    icsbrec_port_binding_index_destroy_row(isb_pb_key);
> >
> >      const struct sbrec_port_binding *sb_pb;
> >      const struct icnbrec_transit_switch *ts;
> > +
> >      ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) {
> >          const struct nbrec_logical_switch *ls = find_ts_in_nb(ctx,
> > ts->name);
> >          if (!ls) {
> >              VLOG_DBG("Transit switch %s not found in NB.", ts->name);
> >              continue;
> >          }
> > -        struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);
> > -        struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);
> > -        struct hmap pb_tnlids = HMAP_INITIALIZER(&pb_tnlids);
> > -        isb_pb_key = icsbrec_port_binding_index_init_row(
> > -            ctx->icsbrec_port_binding_by_ts);
> > -        icsbrec_port_binding_index_set_transit_switch(isb_pb_key,
> > ts->name);
> > +        struct hmap local_pbs = HMAP_INITIALIZER(&local_pbs);
> > +        struct hmap remote_pbs = HMAP_INITIALIZER(&remote_pbs);
> >
> > -        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
> > -
> >  ctx->icsbrec_port_binding_by_ts) {
> > -            if (isb_pb->availability_zone == ctx->runned_az) {
> > -                shash_add(&local_pbs, isb_pb->logical_port, isb_pb);
> > -                shash_find_and_delete(&isb_all_local_pbs,
> > -                                      isb_pb->logical_port);
> > +        const struct icsbrec_port_binding *isb_pb_key;
> > +        isb_pb_key = icsbrec_port_binding_index_init_row(
> > +            ctx->icsbrec_port_binding_by_nb_ic_uuid_type);
> > +        icsbrec_port_binding_index_set_nb_ic_uuid(isb_pb_key,
> > +            ts->header_.uuid);
> > +        icsbrec_port_binding_index_set_type(isb_pb_key, "transit-switch");
> > +        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (current_isb_pb, isb_pb_key,
> > +            ctx->icsbrec_port_binding_by_nb_ic_uuid_type) {
> > +            if (current_isb_pb->availability_zone == ctx->runned_az) {
> > +                struct ic_pb_info *pb_info =
> > ic_pb_remove(&isb_all_local_pbs,
> > +                    &current_isb_pb->nb_ic_uuid,
> > current_isb_pb->logical_port);
> > +                ic_pb_free(pb_info);
> > +                pb_info = ic_pb_create(current_isb_pb);
> > +                ic_pb_insert(&local_pbs, pb_info);
> >              } else {
> > -                shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);
> > +                struct ic_pb_info *pb_info = ic_pb_find(&remote_pbs,
> > +                    &current_isb_pb->nb_ic_uuid,
> > current_isb_pb->logical_port);
> > +                if (!pb_info) {
> > +                    pb_info = ic_pb_create(current_isb_pb);
> > +                    ic_pb_insert(&remote_pbs, pb_info);
> > +                }
> > +
> >              }
> > -            ovn_add_tnlid(&pb_tnlids, isb_pb->tunnel_key);
> >          }
> >          icsbrec_port_binding_index_destroy_row(isb_pb_key);
> >
> > @@ -879,56 +1397,168 @@ port_binding_run(struct ic_context *ctx)
> >              if (!strcmp(lsp->type, "router")
> >                  || !strcmp(lsp->type, "switch")) {
> >                  /* The port is local. */
> > -                sb_pb = find_lsp_in_sb(ctx, lsp);
> > +                sb_pb = find_lp_in_sb(ctx, lsp->name);
> >                  if (!sb_pb) {
> >                      continue;
> >                  }
> > -                isb_pb = shash_find_and_delete(&local_pbs, lsp->name);
> > +
> > +                const struct icsbrec_port_binding *isb_pb;
> > +                isb_pb = ic_pb_find_and_remove(&local_pbs,
> > &ts->header_.uuid,
> > +                    lsp->name);
> >                  if (!isb_pb) {
> >                      uint32_t pb_tnl_key = allocate_port_key(&pb_tnlids);
> > -                    create_isb_pb(ctx, sb_pb, ctx->runned_az,
> > -                                  ts->name, pb_tnl_key);
> > +                    create_isb_pb(ctx, sb_pb, ctx->runned_az, ts->name,
> > +                        &ts->header_.uuid, "transit-switch", NULL,
> > pb_tnl_key);
> >                  } else {
> >                      sync_local_port(ctx, isb_pb, sb_pb, lsp);
> >                  }
> > +
> >              } else if (!strcmp(lsp->type, "remote")) {
> >                  /* The port is remote. */
> > -                isb_pb = shash_find_and_delete(&remote_pbs, lsp->name);
> > +                const struct icsbrec_port_binding *isb_pb;
> > +                isb_pb = ic_pb_find_and_remove(&remote_pbs,
> > &ts->header_.uuid,
> > +                    lsp->name);
> >                  if (!isb_pb) {
> >                      nbrec_logical_switch_update_ports_delvalue(ls, lsp);
> >                  } else {
> > -                    sb_pb = find_lsp_in_sb(ctx, lsp);
> > +                    sb_pb = find_lp_in_sb(ctx, lsp->name);
> >                      if (!sb_pb) {
> >                          continue;
> >                      }
> >                      sync_remote_port(ctx, isb_pb, lsp, sb_pb);
> >                  }
> > +
> >              } else {
> >                  VLOG_DBG("Ignore lsp %s on ts %s with type %s.",
> >                           lsp->name, ts->name, lsp->type);
> >              }
> >          }
> >
> > -        /* Delete extra port-binding from ISB */
> > -        SHASH_FOR_EACH (node, &local_pbs) {
> > -            icsbrec_port_binding_delete(node->data);
> > +        struct ic_pb_info *info;
> > +        HMAP_FOR_EACH_POP (info, node, &local_pbs) {
> > +            const struct icsbrec_port_binding *isb_pb = ic_pb_free(info);
> > +            if (isb_pb) {
> > +                icsbrec_port_binding_delete(isb_pb);
> > +            }
> > +
> >          }
> >
> >          /* Create lsp in NB for remote ports */
> > -        SHASH_FOR_EACH (node, &remote_pbs) {
> > -            create_nb_lsp(ctx, node->data, ls);
> > +        HMAP_FOR_EACH_POP (info, node, &remote_pbs) {
> > +            const struct icsbrec_port_binding *isb_pb;
> > +            isb_pb = ic_pb_free(info);
> > +            create_nb_lsp(ctx, isb_pb, ls);
> >          }
> >
> > -        shash_destroy(&local_pbs);
> > -        shash_destroy(&remote_pbs);
> > -        ovn_destroy_tnlids(&pb_tnlids);
> > +        hmap_destroy(&local_pbs);
> > +        hmap_destroy(&remote_pbs);
> >      }
> >
> > -    SHASH_FOR_EACH (node, &isb_all_local_pbs) {
> > -        icsbrec_port_binding_delete(node->data);
> > +    /* Walk list of transit router ports */
> > +    struct shash trp_ports = SHASH_INITIALIZER(&trp_ports);
> > +    trp_parse(ctx, &trp_ports);
> > +
> > +    const struct icnbrec_transit_router *tr;
> > +    /* Transit Router port binding */
> > +    ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->ovninb_idl) {
> > +        const struct nbrec_logical_router *lr = find_tr_in_nb(ctx,
> > tr->name);
> > +        if (!lr) {
> > +            VLOG_DBG("Transit router %s not found in NB.", tr->name);
> > +            continue;
> > +        }
> > +
> > +        struct hmap local_pbs = HMAP_INITIALIZER(&local_pbs);
> > +        struct hmap remote_pbs = HMAP_INITIALIZER(&remote_pbs);
> > +        const struct icsbrec_port_binding *isb_pb_key;
> > +        isb_pb_key = icsbrec_port_binding_index_init_row(
> > +            ctx->icsbrec_port_binding_by_nb_ic_uuid_type);
> > +        icsbrec_port_binding_index_set_nb_ic_uuid(isb_pb_key,
> > +            tr->header_.uuid);
> > +        icsbrec_port_binding_index_set_type(isb_pb_key, "transit-router");
> > +
> > +        const struct icsbrec_port_binding *isb_pb;
> > +        ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
> > +
> >  ctx->icsbrec_port_binding_by_nb_ic_uuid_type) {
> > +            if (isb_pb->availability_zone == ctx->runned_az) {
> > +                struct ic_pb_info *pb_info =
> > ic_pb_remove(&isb_all_local_pbs,
> > +                    &isb_pb->nb_ic_uuid, isb_pb->logical_port);
> > +                ic_pb_insert(&local_pbs, pb_info);
> > +
> > +            } else {
> > +                struct ic_pb_info *pb_info = ic_pb_find(&remote_pbs,
> > +                    &isb_pb->nb_ic_uuid, isb_pb->logical_port);
> > +                if (!pb_info) {
> > +                    pb_info = ic_pb_create(isb_pb);
> > +                    ic_pb_insert(&remote_pbs, pb_info);
> > +                }
> > +
> > +            }
> > +        }
> > +        icsbrec_port_binding_index_destroy_row(isb_pb_key);
> > +
> > +        for (size_t i = 0; i < lr->n_ports; i++) {
> > +            const struct nbrec_logical_router_port *lrp;
> > +            lrp = lr->ports[i];
> > +
> > +            sb_pb = find_lp_in_sb(ctx, lrp->name);
> > +            if (!sb_pb) {
> > +                continue;
> > +            }
> > +
> > +            /* Check if port came from ICNB */
> > +            struct ic_trp_data *trp_data = shash_find_data(&trp_ports,
> > +                lrp->name);
> > +            if (trp_data) {
> > +                trp_port_sync(ctx, trp_data, ctx->runned_az, &local_pbs,
> > +                    &remote_pbs, &pb_tnlids, sb_pb);
> > +                continue;
> > +            } else {
> > +                if (smap_get(&lrp->options, "interconn-tr")) {
> > +                    nbrec_logical_router_port_delete(lrp);
> > +                    nbrec_logical_router_update_ports_delvalue(lr, lrp);
> > +                }
> > +
> > +            }
> > +
> > +        }
> > +
> > +        struct ic_pb_info *info;
> > +        HMAP_FOR_EACH_POP (info, node, &local_pbs) {
> > +            isb_pb = ic_pb_free(info);
> > +        }
> > +
> > +        /* Create lsp in NB for remote ports */
> > +        HMAP_FOR_EACH_POP (info, node, &remote_pbs) {
> > +            isb_pb = ic_pb_free(info);
> > +        }
> > +
> > +        hmap_destroy(&local_pbs);
> > +        hmap_destroy(&remote_pbs);
> > +    }
> > +    ovn_destroy_tnlids(&pb_tnlids);
> > +
> > +    struct ic_pb_info *info;
> > +    HMAP_FOR_EACH_SAFE (info, node, &isb_all_local_pbs) {
> > +        const struct icsbrec_port_binding *isb_pb;
> > +        isb_pb = ic_pb_remove_node_and_free(&isb_all_local_pbs, info);
> > +        if (isb_pb) {
> > +            icsbrec_port_binding_delete(isb_pb);
> > +        }
> > +
> >      }
> >
> > -    shash_destroy(&isb_all_local_pbs);
> > +    hmap_destroy(&isb_all_local_pbs);
> > +
> > +    struct shash_node *node;
> > +    SHASH_FOR_EACH (node, &trp_ports) {
> > +        struct ic_trp_data *trp_port = node->data;
> > +        free(trp_port->name);
> > +        free(trp_port->mac);
> > +        free(trp_port->tr_name);
> > +        free(trp_port->chassis);
> > +        free(trp_port);
> > +    }
> > +    shash_destroy(&trp_ports);
> >  }
> >
> >  struct ic_router_info {
> > @@ -1609,20 +2239,6 @@ get_lrp_name_by_ts_port_name(struct ic_context
> > *ctx, const char *ts_port_name)
> >      return smap_get(&nb_lsp->options, "router-port");
> >  }
> >
> > -static const struct nbrec_logical_router_port *
> > -get_lrp_by_lrp_name(struct ic_context *ctx, const char *lrp_name)
> > -{
> > -    const struct nbrec_logical_router_port *lrp;
> > -    const struct nbrec_logical_router_port *lrp_key =
> > -        nbrec_logical_router_port_index_init_row(ctx->nbrec_lrp_by_name);
> > -    nbrec_logical_router_port_index_set_name(lrp_key, lrp_name);
> > -    lrp = nbrec_logical_router_port_index_find(ctx->nbrec_lrp_by_name,
> > -                                               lrp_key);
> > -    nbrec_logical_router_port_index_destroy_row(lrp_key);
> > -
> > -    return lrp;
> > -}
> > -
> >  static const struct nbrec_logical_router_port *
> >  find_lrp_of_nexthop(struct ic_context *ctx,
> >                      const struct icsbrec_route *isb_route)
> > @@ -1728,12 +2344,14 @@ sync_learned_routes(struct ic_context *ctx,
> >              route_filter_tag = "";
> >          }
> >
> > -        isb_route_key =
> > icsbrec_route_index_init_row(ctx->icsbrec_route_by_ts);
> > -        icsbrec_route_index_set_transit_switch(isb_route_key,
> > -                                               isb_pb->transit_switch);
> > +        isb_route_key = icsbrec_route_index_init_row(
> > +            ctx->icsbrec_route_by_nb_ic_uuid_type);
> > +        icsbrec_route_index_set_nb_ic_uuid(isb_route_key,
> > +                                               isb_pb->nb_ic_uuid);
> > +        icsbrec_route_index_set_type(isb_route_key, isb_pb->type);
> >
> >          ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,
> > -                                      ctx->icsbrec_route_by_ts) {
> > +
> > ctx->icsbrec_route_by_nb_ic_uuid_type) {
> >              const char *lr_id = smap_get(&isb_route->external_ids,
> > "lr-id");
> >              if (lr_id == NULL) {
> >                  continue;
> > @@ -1875,22 +2493,41 @@ ad_route_sync_external_ids(const struct
> > ic_route_info *route_adv,
> >      }
> >  }
> >
> > +struct ic_route {
> > +    struct hmap_node node;
> > +    struct uuid nb_ic_uuid;
> > +    struct hmap routes_ad;
> > +    const char *ts_name;
> > +    const char *type;
> > +};
> >
> 
> As mentioned on the schema, we shouldn't change the
> routes at all for now.
Ack, Ill undo all route changes
> 
> 
> > +
> > +static struct ic_route *
> > +ic_route_ad_find(struct hmap *route, const struct uuid *nb_ic_uuid)
> > +{
> > +    struct ic_route *ic_route;
> > +    size_t hash = uuid_hash(nb_ic_uuid);
> > +    HMAP_FOR_EACH_WITH_HASH (ic_route, node, hash, route) {
> > +        if (uuid_equals(nb_ic_uuid, &ic_route->nb_ic_uuid)) {
> > +            return ic_route;
> > +        }
> > +    }
> > +    return NULL;
> > +}
> > +
> >  /* Sync routes from routes_ad to IC-SB. */
> >  static void
> >  advertise_routes(struct ic_context *ctx,
> > -                 const struct icsbrec_availability_zone *az,
> > -                 const char *ts_name,
> > -                 struct hmap *routes_ad)
> > +                 struct ic_route *route)
> >  {
> >      ovs_assert(ctx->ovnisb_txn);
> >      const struct icsbrec_route *isb_route;
> >      const struct icsbrec_route *isb_route_key =
> > -        icsbrec_route_index_init_row(ctx->icsbrec_route_by_ts_az);
> > -    icsbrec_route_index_set_transit_switch(isb_route_key, ts_name);
> > -    icsbrec_route_index_set_availability_zone(isb_route_key, az);
> > +        icsbrec_route_index_init_row(ctx->icsbrec_route_by_nb_ic_az);
> > +    icsbrec_route_index_set_nb_ic_uuid(isb_route_key, route->nb_ic_uuid);
> > +    icsbrec_route_index_set_availability_zone(isb_route_key,
> > ctx->runned_az);
> >
> >      ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,
> > -                                  ctx->icsbrec_route_by_ts_az) {
> > +                                  ctx->icsbrec_route_by_nb_ic_az) {
> >          struct in6_addr prefix, nexthop;
> >          unsigned int plen;
> >
> > @@ -1904,7 +2541,7 @@ advertise_routes(struct ic_context *ctx,
> >              continue;
> >          }
> >          struct ic_route_info *route_adv =
> > -            ic_route_find(routes_ad, &prefix, plen, &nexthop,
> > +            ic_route_find(&route->routes_ad, &prefix, plen, &nexthop,
> >                            isb_route->origin, isb_route->route_table, 0);
> >          if (!route_adv) {
> >              /* Delete the extra route from IC-SB. */
> > @@ -1915,7 +2552,7 @@ advertise_routes(struct ic_context *ctx,
> >          } else {
> >              ad_route_sync_external_ids(route_adv, isb_route);
> >
> > -            hmap_remove(routes_ad, &route_adv->node);
> > +            hmap_remove(&route->routes_ad, &route_adv->node);
> >              free(route_adv);
> >          }
> >      }
> > @@ -1923,10 +2560,12 @@ advertise_routes(struct ic_context *ctx,
> >
> >      /* Create the missing routes in IC-SB */
> >      struct ic_route_info *route_adv;
> > -    HMAP_FOR_EACH_SAFE (route_adv, node, routes_ad) {
> > +    HMAP_FOR_EACH_SAFE (route_adv, node, &route->routes_ad) {
> >          isb_route = icsbrec_route_insert(ctx->ovnisb_txn);
> > -        icsbrec_route_set_transit_switch(isb_route, ts_name);
> > -        icsbrec_route_set_availability_zone(isb_route, az);
> > +        icsbrec_route_set_availability_zone(isb_route, ctx->runned_az);
> > +        icsbrec_route_set_type(isb_route, route->type);
> > +        icsbrec_route_set_transit_switch(isb_route, route->ts_name);
> > +        icsbrec_route_set_nb_ic_uuid(isb_route, route->nb_ic_uuid);
> >
> >          char *prefix_s, *nexthop_s;
> >          if (IN6_IS_ADDR_V4MAPPED(&route_adv->prefix)) {
> > @@ -1954,7 +2593,7 @@ advertise_routes(struct ic_context *ctx,
> >
> >          ad_route_sync_external_ids(route_adv, isb_route);
> >
> > -        hmap_remove(routes_ad, &route_adv->node);
> > +        hmap_remove(&route->routes_ad, &route_adv->node);
> >          free(route_adv);
> >      }
> >  }
> > @@ -2045,19 +2684,19 @@ build_ts_routes_to_adv(struct ic_context *ctx,
> >  static void
> >  collect_lr_routes(struct ic_context *ctx,
> >                    struct ic_router_info *ic_lr,
> > -                  struct shash *routes_ad_by_ts)
> > +                  struct hmap *routes_ad_by_ts)
> >  {
> >      const struct nbrec_nb_global *nb_global =
> >          nbrec_nb_global_first(ctx->ovnnb_idl);
> >      ovs_assert(nb_global);
> >
> >      const struct icsbrec_port_binding *isb_pb;
> > -    const char *lrp_name, *ts_name, *route_table, *route_tag;
> > +    const char *lrp_name, *route_table, *route_tag;
> >      struct lport_addresses ts_port_addrs;
> >      const struct icnbrec_transit_switch *key;
> >      const struct nbrec_logical_router_port *lrp;
> >
> > -    struct hmap *routes_ad;
> > +    struct ic_route *routes_ad;
> >      const struct icnbrec_transit_switch *t_sw;
> >      VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {
> >          key = icnbrec_transit_switch_index_init_row(
> > @@ -2069,12 +2708,16 @@ collect_lr_routes(struct ic_context *ctx,
> >          if (!t_sw) {
> >              continue;
> >          }
> > -        ts_name = t_sw->name;
> > -        routes_ad = shash_find_data(routes_ad_by_ts, ts_name);
> > +        routes_ad = ic_route_ad_find(routes_ad_by_ts,
> > &t_sw->header_.uuid);
> > +
> >          if (!routes_ad) {
> >              routes_ad = xzalloc(sizeof *routes_ad);
> > -            hmap_init(routes_ad);
> > -            shash_add(routes_ad_by_ts, ts_name, routes_ad);
> > +            hmap_init(&routes_ad->routes_ad);
> > +            routes_ad->nb_ic_uuid = isb_pb->nb_ic_uuid;
> > +            routes_ad->ts_name = xstrdup(t_sw->name);
> > +            routes_ad->type = xstrdup(isb_pb->type);
> > +            hmap_insert(routes_ad_by_ts, &routes_ad->node,
> > +                uuid_hash(&routes_ad->nb_ic_uuid));
> >          }
> >
> >          if (!extract_lsp_addresses(isb_pb->address, &ts_port_addrs)) {
> > @@ -2094,32 +2737,35 @@ collect_lr_routes(struct ic_context *ctx,
> >              route_table = "";
> >              route_tag = "";
> >          }
> > -        build_ts_routes_to_adv(ctx, ic_lr, routes_ad, &ts_port_addrs,
> > -                               nb_global, route_table, route_tag, lrp);
> > +        build_ts_routes_to_adv(ctx, ic_lr, &routes_ad->routes_ad,
> > +                    &ts_port_addrs, nb_global, route_table, route_tag,
> > lrp);
> >          destroy_lport_addresses(&ts_port_addrs);
> >      }
> >  }
> >
> >  static void
> > -delete_orphan_ic_routes(struct ic_context *ctx,
> > -                         const struct icsbrec_availability_zone *az)
> > +delete_orphan_ic_routes(struct ic_context *ctx)
> >  {
> >      const struct icsbrec_route *isb_route, *isb_route_key =
> >          icsbrec_route_index_init_row(ctx->icsbrec_route_by_az);
> > -    icsbrec_route_index_set_availability_zone(isb_route_key, az);
> > -
> > -    const struct icnbrec_transit_switch *t_sw, *t_sw_key;
> > +    icsbrec_route_index_set_availability_zone(isb_route_key,
> > ctx->runned_az);
> >
> >      ICSBREC_ROUTE_FOR_EACH_EQUAL (isb_route, isb_route_key,
> >                                    ctx->icsbrec_route_by_az)
> >      {
> > -        t_sw_key = icnbrec_transit_switch_index_init_row(
> > -            ctx->icnbrec_transit_switch_by_name);
> > -        icnbrec_transit_switch_index_set_name(t_sw_key,
> > -            isb_route->transit_switch);
> > -        t_sw = icnbrec_transit_switch_index_find(
> > -            ctx->icnbrec_transit_switch_by_name, t_sw_key);
> > -        icnbrec_transit_switch_index_destroy_row(t_sw_key);
> > +        if (isb_route->type && !strcmp(isb_route->type,
> > "transit-router")) {
> > +            const struct icnbrec_transit_router *t_rt;
> > +
> > +            t_rt = icnbrec_transit_router_get_for_uuid(ctx->ovninb_idl,
> > +                &isb_route->nb_ic_uuid);
> > +            if (!t_rt) {
> > +                icsbrec_route_delete(isb_route);
> > +            }
> > +            continue;
> > +        }
> > +        const struct icnbrec_transit_switch *t_sw;
> > +        t_sw = icnbrec_transit_switch_get_for_uuid(ctx->ovninb_idl,
> > +            &isb_route->nb_ic_uuid);
> >
> >          if (!t_sw || !find_lrp_of_nexthop(ctx, isb_route)) {
> >              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> > @@ -2140,7 +2786,7 @@ route_run(struct ic_context *ctx)
> >          return;
> >      }
> >
> > -    delete_orphan_ic_routes(ctx, ctx->runned_az);
> > +    delete_orphan_ic_routes(ctx);
> >
> >      struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs);
> >      const struct icsbrec_port_binding *isb_pb;
> > @@ -2156,8 +2802,10 @@ route_run(struct ic_context *ctx)
> >      ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
> >                                           ctx->icsbrec_port_binding_by_az)
> >      {
> > +        if (isb_pb->type && !strcmp(isb_pb->type, "transit-router")) {
> > +            continue;
> > +        }
> >          const struct nbrec_logical_switch_port *nb_lsp;
> > -
> >          nb_lsp = get_lsp_by_ts_port_name(ctx, isb_pb->logical_port);
> >          if (!strcmp(nb_lsp->type, "switch")) {
> >              VLOG_DBG("IC-SB Port_Binding '%s' on ts '%s' corresponds to a
> > "
> > @@ -2204,7 +2852,7 @@ route_run(struct ic_context *ctx)
> >      icsbrec_port_binding_index_destroy_row(isb_pb_key);
> >
> >      struct ic_router_info *ic_lr;
> > -    struct shash routes_ad_by_ts = SHASH_INITIALIZER(&routes_ad_by_ts);
> > +    struct hmap routes_ad_by_ts = HMAP_INITIALIZER(&routes_ad_by_ts);
> >      HMAP_FOR_EACH_SAFE (ic_lr, node, &ic_lrs) {
> >          collect_lr_routes(ctx, ic_lr, &routes_ad_by_ts);
> >          sync_learned_routes(ctx, ic_lr);
> > @@ -2213,12 +2861,17 @@ route_run(struct ic_context *ctx)
> >          hmap_remove(&ic_lrs, &ic_lr->node);
> >          free(ic_lr);
> >      }
> > -    struct shash_node *node;
> > -    SHASH_FOR_EACH (node, &routes_ad_by_ts) {
> > -        advertise_routes(ctx, ctx->runned_az, node->name, node->data);
> > -        hmap_destroy(node->data);
> > -    }
> > -    shash_destroy_free_data(&routes_ad_by_ts);
> > +
> > +    struct ic_route *node;
> > +    HMAP_FOR_EACH_SAFE (node, node, &routes_ad_by_ts) {
> > +        advertise_routes(ctx, node);
> > +        hmap_destroy(&node->routes_ad);
> > +        free((char *) node->ts_name);
> > +        free((char *) node->type);
> > +        free(node);
> > +    };
> > +
> > +    hmap_destroy(&routes_ad_by_ts);
> >      hmap_destroy(&ic_lrs);
> >  }
> >
> > @@ -2733,11 +3386,29 @@ 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 hmap isb_ts_dps = HMAP_INITIALIZER(&isb_ts_dps);
> > +    struct hmap isb_tr_dps = HMAP_INITIALIZER(&isb_tr_dps);
> > +
> >      gateway_run(ctx);
> > -    ts_run(ctx);
> > +    ts_run(ctx, &dp_tnlids, &isb_ts_dps, &isb_tr_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 ic_dp_info *dps;
> > +    HMAP_FOR_EACH_SAFE (dps, node, &isb_ts_dps) {
> > +        free(dps);
> > +    }
> > +    hmap_destroy(&isb_ts_dps);
> > +
> > +    HMAP_FOR_EACH_SAFE (dps, node, &isb_tr_dps) {
> > +        free(dps);
> > +    }
> > +    hmap_destroy(&isb_tr_dps);
> >  }
> >
> >  static void
> > @@ -3105,6 +3776,9 @@ main(int argc, char *argv[])
> >      struct ovsdb_idl_index *nbrec_ls_by_name
> >          = ovsdb_idl_index_create1(ovnnb_idl_loop.idl,
> >                                    &nbrec_logical_switch_col_name);
> > +    struct ovsdb_idl_index *nbrec_lr_by_name
> > +        = ovsdb_idl_index_create1(ovnnb_idl_loop.idl,
> > +                                  &nbrec_logical_router_col_name);
> >      struct ovsdb_idl_index *nbrec_port_by_name
> >          = ovsdb_idl_index_create1(ovnnb_idl_loop.idl,
> >                                    &nbrec_logical_switch_port_col_name);
> > @@ -3114,6 +3788,7 @@ main(int argc, char *argv[])
> >      struct ovsdb_idl_index *sbrec_port_binding_by_name
> >          = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> >                                    &sbrec_port_binding_col_logical_port);
> > +
> >      struct ovsdb_idl_index *sbrec_chassis_by_name
> >          = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> >                                    &sbrec_chassis_col_name);
> > @@ -3139,26 +3814,27 @@ main(int argc, char *argv[])
> >          = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
> >
> >  &icsbrec_port_binding_col_availability_zone);
> >
> > -    struct ovsdb_idl_index *icsbrec_port_binding_by_ts
> > +    struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid
> >          = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
> > -
> > &icsbrec_port_binding_col_transit_switch);
> > +                                  &icsbrec_port_binding_col_nb_ic_uuid);
> >
> > -    struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az
> > +    struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid_type
> >          = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,
> > -
> > &icsbrec_port_binding_col_transit_switch,
> > -
> > &icsbrec_port_binding_col_availability_zone);
> > +                                  &icsbrec_port_binding_col_nb_ic_uuid,
> > +                                  &icsbrec_port_binding_col_type);
> >
> >      struct ovsdb_idl_index *icsbrec_route_by_az
> >          = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
> >                                    &icsbrec_route_col_availability_zone);
> >
> > -    struct ovsdb_idl_index *icsbrec_route_by_ts
> > -        = ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
> > -                                  &icsbrec_route_col_transit_switch);
> > +    struct ovsdb_idl_index *icsbrec_route_by_nb_ic_uuid_type
> > +        = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,
> > +                                  &icsbrec_route_col_nb_ic_uuid,
> > +                                  &icsbrec_route_col_type);
> >
> > -    struct ovsdb_idl_index *icsbrec_route_by_ts_az
> > +    struct ovsdb_idl_index *icsbrec_route_by_nb_ic_az
> >          = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,
> > -                                  &icsbrec_route_col_transit_switch,
> > +                                  &icsbrec_route_col_nb_ic_uuid,
> >                                    &icsbrec_route_col_availability_zone);
> >
> >      struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az
> > @@ -3221,6 +3897,7 @@ main(int argc, char *argv[])
> >                  .ovnisb_idl = ovnisb_idl_loop.idl,
> >                  .ovnisb_txn = ovsdb_idl_loop_run(&ovnisb_idl_loop),
> >                  .nbrec_ls_by_name = nbrec_ls_by_name,
> > +                .nbrec_lr_by_name = nbrec_lr_by_name,
> >                  .nbrec_lrp_by_name = nbrec_lrp_by_name,
> >                  .nbrec_port_by_name = nbrec_port_by_name,
> >                  .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
> > @@ -3234,17 +3911,20 @@ main(int argc, char *argv[])
> >                  .icnbrec_transit_switch_by_name =
> >                      icnbrec_transit_switch_by_name,
> >                  .icsbrec_port_binding_by_az = icsbrec_port_binding_by_az,
> > -                .icsbrec_port_binding_by_ts = icsbrec_port_binding_by_ts,
> > -                .icsbrec_port_binding_by_ts_az =
> > icsbrec_port_binding_by_ts_az,
> > +                .icsbrec_port_binding_by_nb_ic_uuid =
> > +                    icsbrec_port_binding_by_nb_ic_uuid,
> > +                .icsbrec_port_binding_by_nb_ic_uuid_type =
> > +                icsbrec_port_binding_by_nb_ic_uuid_type,
> >                  .icsbrec_route_by_az = icsbrec_route_by_az,
> > -                .icsbrec_route_by_ts = icsbrec_route_by_ts,
> > -                .icsbrec_route_by_ts_az = icsbrec_route_by_ts_az,
> >                  .icsbrec_service_monitor_by_source_az =
> >                      icsbrec_service_monitor_by_source_az,
> >                  .icsbrec_service_monitor_by_target_az =
> >                      icsbrec_service_monitor_by_target_az,
> >                  .icsbrec_service_monitor_by_target_az_logical_port =
> >                      icsbrec_service_monitor_by_target_az_logical_port,
> > +                .icsbrec_route_by_nb_ic_uuid_type =
> > +                    icsbrec_route_by_nb_ic_uuid_type,
> > +                .icsbrec_route_by_nb_ic_az = icsbrec_route_by_nb_ic_az,
> >              };
> >
> >              if (!state.had_lock &&
> > ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
> > 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.
> >    </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/ovn-ic-nb.ovsschema b/ovn-ic-nb.ovsschema
> > index 8145b0335..c1eefb25f 100644
> > --- a/ovn-ic-nb.ovsschema
> > +++ b/ovn-ic-nb.ovsschema
> > @@ -1,7 +1,7 @@
> >  {
> >      "name": "OVN_IC_Northbound",
> > -    "version": "1.2.0",
> > -    "cksum": "4176728051 3557",
> > +    "version": "1.2.1",
> >
> 
> This is a pretty big change so we should bump the Y version e.g.
> 1.3.0.
Ack, Ill change the version
> 
> 
> > +    "cksum": "471978705 4834",
> >      "tables": {
> >          "IC_NB_Global": {
> >              "columns": {
> > @@ -35,6 +35,34 @@
> >                               "min": 0, "max": "unlimited"}}},
> >              "isRoot": true,
> >              "indexes": [["name"]]},
> > +        "Transit_Router": {
> > +            "columns": {
> > +                "name": {"type": "string"},
> > +                "other_config": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}},
> > +                "external_ids": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}}},
> > +            "isRoot": true,
> > +            "indexes": [["name"]]},
> > +        "Transit_Router_Port": {
> > +            "columns": {
> > +                "name": {"type": "string"},
> > +                "other_config": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}},
> > +                "mac": {"type": "string"},
> > +                "chassis": {"type": "string"},
> >
> +                "networks": {"type": {"key": "string",
> > +                                      "min": 0,
> > +                                      "max": "unlimited"}},
> > +                "nb_ic_uuid": {"type": {"key": {"type": "uuid"},
> > +                                     "min": 1,
> > +                                     "max": 1}},
> > +                "transit_router": {"type": "string"}},
> > +            "isRoot": true,
> > +            "indexes": [["name"]]},
> >          "Connection": {
> >              "columns": {
> >                  "target": {"type": "string"},
> > diff --git a/ovn-ic-nb.xml b/ovn-ic-nb.xml
> > index 304e100ff..90d4694c4 100644
> > --- a/ovn-ic-nb.xml
> > +++ b/ovn-ic-nb.xml
> > @@ -120,6 +120,76 @@
> >      </group>
> >    </table>
> >
> > +  <table name="Transit_Router" title="Transit logical router">
> > +    <p>
> > +      Each row represents one transit logical router for interconnection
> > +      between different OVN deployments (availability zones).
> > +    </p>
> > +
> > +    <group title="Naming">
> > +      <column name="name">
> > +        A name that uniquely identifies the transit logical router.
> > +      </column>
> > +    </group>
> > +
> > +    <group title="Common Columns">
> > +      <column name="other_config"/>
> > +      <column name="external_ids">
> > +        See <em>External IDs</em> at the beginning of this document.
> > +      </column>
> > +    </group>
> > +  </table>
> > +
> > +  <table name="Transit_Router_Port" title="Transit logical router">
> > +    <p>
> > +      Each row represents one transit logical router for interconnection
> > +      between different OVN deployments (availability zones).
> > +    </p>
> > +
> > +    <group title="Naming">
> > +      <column name="name">
> > +        A name that uniquely identifies the transit logical router port.
> > +      </column>
> > +    </group>
> > +
> > +    <column name="mac">
> > +      The Ethernet address that belongs to this router port.
> > +    </column>
> > +
> > +    <column name="chassis">
> > +      The chassis this router port should be bound to.
> > +    </column>
> > +
> > +    <column name="nb_ic_uuid">
> > +       This is the UUID of the northbound IC logical datapath this port is
> > +       associated with.
> > +    </column>
> > +
> > +    <column name="transit_router">
> > +       The name of the <code>Transit_Router</code> this port belongs to.
> > +    </column>
> > +
> > +    <column name="networks">
> > +      <p>
> > +        The IP addresses and netmasks of the router port.  For example,
> > +        <code>192.168.0.1/24</code> indicates that the router's IP
> > +        address is 192.168.0.1 and that packets destined to
> > +        192.168.0.<var>x</var> should be routed to this port.
> > +        These are optional.
> > +      </p>
> > +
> > +      <p>
> > +        A logical router port always adds a link-local IPv6 address
> > +        (fe80::/64) automatically generated from the interface's MAC
> > +        address using the modified EUI-64 format.
> > +      </p>
> > +    </column>
> > +
> > +    <group title="Common Columns">
> > +      <column name="other_config"/>
> > +    </group>
> > +  </table>
> > +
> >    <table name="SSL">
> >      SSL/TLS configuration for ovn-nb database access.
> >
> > diff --git a/ovn-ic-sb.ovsschema b/ovn-ic-sb.ovsschema
> > index 34b5457bb..5880729e8 100644
> > --- a/ovn-ic-sb.ovsschema
> > +++ b/ovn-ic-sb.ovsschema
> > @@ -1,7 +1,7 @@
> >  {
> >      "name": "OVN_IC_Southbound",
> >      "version": "2.2.0",
> >
> 
> The version bump here is definitely required.
Ack, Ill change the version
> 
> 
> > -    "cksum": "2294868959 8438",
> > +    "cksum": "662060257 9716",
> >      "tables": {
> >          "IC_SB_Global": {
> >              "columns": {
> > @@ -58,6 +58,13 @@
> >          "Datapath_Binding": {
> >              "columns": {
> >                  "transit_switch": {"type": "string"},
> > +                "type": {"type": {"key": {"type": "string",
> > +                                          "enum": ["set",
> > +                                                      ["transit-switch",
> > +
> >  "transit-router"]]}}},
> >
> 
> This suffers the same issue as the datapath refactor in SB.
> We should specify {"min": 0, "max": 1} to allow empty values.
Ack, ill add min/max to preserve backwards compatability
> 
> +                "nb_ic_uuid": {"type": {"key": {"type": "uuid"},
> > +                                     "min": 1,
> > +                                     "max": 1}},
> >
> 
> Same here, the min should be 0.
Ack, ill add min/max to preserve backwards compatability
> 
> 
> >                  "tunnel_key": {
> >                       "type": {"key": {"type": "integer",
> >                                        "minInteger": 1,
> > @@ -65,7 +72,7 @@
> >                  "external_ids": {
> >                      "type": {"key": "string", "value": "string",
> >                               "min": 0, "max": "unlimited"}}},
> > -            "indexes": [["tunnel_key"]],
> > +            "indexes": [["nb_ic_uuid"],["tunnel_key"]],
> >              "isRoot": true},
> >          "Port_Binding": {
> >              "columns": {
> > @@ -83,15 +90,29 @@
> >                                               "refType": "weak"},
> >                                      "min": 0, "max": 1}},
> >                  "address": {"type": "string"},
> > +                "type": {"type": {"key": {"type": "string",
> > +                                          "enum": ["set",
> > +                                                      ["transit-switch",
> > +
> >  "transit-router"]]}}},
> >
> 
> Same here, also I would prefer "transit-switch-port" and
> "transit-router-port".
Ack, Ill change to the names above
> 
> 
> > +                "nb_ic_uuid": {"type": {"key": {"type": "uuid"},
> > +                                     "min": 1,
> > +                                     "max": 1}},
> >
> 
> Same here with the min.
Ack, ill add min/max to preserve backwards compatability
> 
> 
> >                  "tunnel_key": {
> >                       "type": {"key": {"type": "integer",
> >                                        "minInteger": 1,
> > @@ -65,7 +72,7 @@
> >                  "external_ids": {
> >                      "type": {"key": "string", "value": "string",
> >                               "min": 0, "max": "unlimited"}}},
> > -            "indexes": [["tunnel_key"]],
> > +            "indexes": [["nb_ic_uuid"],["tunnel_key"]],
> >              "isRoot": true},
> >          "Port_Binding": {
> >              "columns": {
> > @@ -83,15 +90,29 @@
> >                                               "refType": "weak"},
> >                                      "min": 0, "max": 1}},
> >                  "address": {"type": "string"},
> > +                "type": {"type": {"key": {"type": "string",
> > +                                          "enum": ["set",
> > +                                                      ["transit-switch",
> > +
> >  "transit-router"]]}}},
> >
> 
> Same here, also I would prefer "transit-switch-port" and
> "transit-router-port".
Ack, Ill change to the names above
> 
> 
> > +                "nb_ic_uuid": {"type": {"key": {"type": "uuid"},
> > +                                     "min": 1,
> > +                                     "max": 1}},
> >
> 
> Same here with the min.
Ack, ill add min/max to preserve backwards compatability
> 
> 
> >                  "external_ids": {"type": {"key": "string",
> >                                   "value": "string",
> >                                   "min": 0,
> >                                   "max": "unlimited"}}},
> 
> 
> >                  "external_ids": {"type": {"key": "string",
> >                                   "value": "string",
> >                                   "min": 0,
> >                                   "max": "unlimited"}}},
> > -            "indexes": [["transit_switch", "tunnel_key"],
> > ["logical_port"]],
> > +            "indexes": [["nb_ic_uuid", "tunnel_key"]],
> >              "isRoot": true},
> >          "Route": {
> >              "columns": {
> >                  "transit_switch": {"type": "string"},
> > +                "type": {"type": {"key": {"type": "string",
> > +                                          "enum": ["set",
> > +                                                      ["transit-switch",
> > +
> >  "transit-router"]]}}},
> > +                "nb_ic_uuid": {"type": {"key": {"type": "uuid"},
> > +                                     "min": 1,
> > +                                     "max": 1}},
> >
> 
> We shouldn't change the route at all for now.
Ack, Ill undo route changes
> 
> 
> >                  "availability_zone": {"type": {"key": {"type": "uuid",
> >                                        "refTable": "Availability_Zone"}}},
> >                  "route_table": {"type": "string"},
> > @@ -104,8 +125,8 @@
> >                  "external_ids": {
> >                      "type": {"key": "string", "value": "string",
> >                               "min": 0, "max": "unlimited"}}},
> > -            "indexes": [["transit_switch", "availability_zone",
> > "route_table",
> > -                         "ip_prefix", "nexthop"]],
> > +            "indexes": [["nb_ic_uuid", "type", "availability_zone",
> > +                "route_table", "ip_prefix", "nexthop"]],
> >              "isRoot": true},
> >          "Connection": {
> >              "columns": {
> > diff --git a/ovn-ic-sb.xml b/ovn-ic-sb.xml
> > index 35dc1f509..2e466271b 100644
> > --- a/ovn-ic-sb.xml
> > +++ b/ovn-ic-sb.xml
> > @@ -213,6 +213,15 @@
> >        db="OVN_IC_Northbound"/> table.
> >      </column>
> >
> > +    <column name="type">
> > +      Can be one of <code>transit-switch</code> or
> > <code>transit-router</code>
> > +    </column>
> > +
> > +    <column name="nb_ic_uuid" type='{"type": "string"}'>
> > +      This is the UUID of the corresponding northbound IC logical datapath
> > +      represented by this southbound <code>Datapath_Binding</code>.
> > +    </column>
> > +
> >      <column name="tunnel_key">
> >        <p>
> >          The tunnel key value to which the logical datapath is bound.  The
> > key
> > @@ -302,6 +311,15 @@
> >          </p>
> >        </column>
> >
> > +    <column name="type">
> > +      Can be one of <code>switch-port</code> or <code>router-port</code>
> > +    </column>
> > +
> > +    <column name="nb_ic_uuid" type='{"type": "string"}'>
> > +      This is the UUID of the corresponding northbound IC logical datapath
> > +      represented by this southbound <code>Datapath_Binding</code>.
> > +    </column>
> > +
> >      </group>
> >
> >      <group title="Common Columns">
> > @@ -323,6 +341,16 @@
> >          The name of the transit switch, upon which the route is
> > advertised.
> >        </column>
> >
> > +      <column name="type">
> > +        Can be one of <code>transit-switch</code> or
> > +        <code>transit-router</code>
> > +      </column>
> > +
> > +      <column name="nb_ic_uuid" type='{"type": "string"}'>
> > +        This is the UUID of the corresponding northbound IC logical
> > datapath
> > +        represented by this southbound <code>Datapath_Binding</code>.
> > +      </column>
> > +
> >        <column name="availability_zone">
> >          The availability zone that has advertised the route.
> >        </column>
> > diff --git a/tests/ovn-ic-nbctl.at b/tests/ovn-ic-nbctl.at
> > index 2402d646c..058d66cb1 100644
> > --- a/tests/ovn-ic-nbctl.at
> > +++ b/tests/ovn-ic-nbctl.at
> > @@ -63,3 +63,44 @@ 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-list | ovn-ic-nbctl tr-list | cut -d' ' -f2 |
> > sort], [0], [dnl
> > +(tr0)
> > +(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
> >
> 
> This testing is also missing TRP related commands.
Ack, ill add TRP releated tests
> 
> 
> > diff --git a/tests/ovn-ic-sbctl.at b/tests/ovn-ic-sbctl.at
> > index 86d6b00f6..e61de5729 100644
> > --- a/tests/ovn-ic-sbctl.at
> > +++ b/tests/ovn-ic-sbctl.at
> > @@ -33,7 +33,7 @@ for az in 1 2; do
> >      for pb in 1 2; do
> >        ovn-ic-sbctl create port_binding logical_port=lp$az$gw$pb
> > transit_switch="ts$pb" \
> >          address="\"aa:aa:aa:aa:0$az:$gw$pb 169.254.$pb.$az$gw/24\""
> > tunnel_key=$az$gw \
> > -        availability_zone=$az_uuid gateway=gw$az$gw
> > +        availability_zone=$az_uuid gateway=gw$az$gw type=transit-switch
> > nb_ic_uuid=b4b13365-2fef-4288-8e80-40663dc408$az$pb
> >      done
> >    done
> >  done
> > diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
> > index 2d92f3adf..d1fc56bf0 100644
> > --- a/tests/ovn-ic.at
> > +++ b/tests/ovn-ic.at
> > @@ -912,7 +912,7 @@ check ovn_as az1 ovn-nbctl lb-add lb_v6 [[4242::1]]:80
> > "[[4242::2]]:80"
> >  check ovn_as az1 ovn-nbctl lr-lb-add lr1 lb_v6
> >  OVS_WAIT_UNTIL([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep learned |
> > grep 4242])
> >
> > -AT_CHECK([ovn-ic-sbctl list route | grep 'ip_prefix.*4242' -A 2], [0],
> > [dnl
> > +AT_CHECK([ovn-ic-sbctl list route | grep 'ip_prefix.*4242' -A 3 | sed
> > '/nb_ic_uuid/d'], [0], [dnl
> >  ip_prefix           : "4242::1/128"
> >  nexthop             : "2001:db8:1::1"
> >  origin              : loadbalancer
> > @@ -4248,6 +4248,205 @@ 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
> > +
> > +OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
> >
> 
> Please don't wait for a specific line count. This should be handled
> by the sync call below.
Ack, ill change this to a sync call
> 
> 
> > +
> > +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
> > +OVS_WAIT_UNTIL([test 6 = `ovn-sbctl list Datapath_Binding | wc -l`])
> >
> 
> Wouldn't it be better to use  wait_row_count?
> I won't repeat myself but this applies to all testing in this file.
> 
> +AT_CHECK([ovn-nbctl lr-list | cut -c 38-], [0], [dnl
> >
> 
Ack, Ill change to check_row_cout
> I would rather see the full list instead of using cut, the order
> can also change.
> Maybe even better would be to call:
> "check_row_count nb:Logical_Router 1 name=tr0".
> This also applied to the rest of the testing in this file.
> 
> 
> > +(tr0)
> > +])
> > +
> > +ovn_as az2
> > +OVS_WAIT_UNTIL([test 6 = `ovn-sbctl list Datapath_Binding | wc -l`])
> > +AT_CHECK([ovn-nbctl lr-list | cut -c 38-], [0], [dnl
> > +(tr0)
> > +])
> >
> +
> > +OVN_CLEANUP_IC([az1], [az2])
> > +AT_CLEANUP
> > +])
> > +
> > +
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([ovn-ic -- Add transit router port])
> > +
> > +ovn_init_ic_db
> > +ovn_start az1
> > +ovn_start az2
> > +
> > +OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
> >
> +
> > +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
> > +check ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
> > 192.168.10.20/24
> > +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> >
> +AT_CHECK([ovn-sbctl list Port_Binding | grep tr0-p0], [0], [dnl
> > +logical_port        : tr0-p0
> > +])
> >
> +
> > +ovn_as az2
> > +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep tr0-p0], [0], [dnl
> > +logical_port        : tr0-p0
> > +])
> >
> +
> > +OVN_CLEANUP_IC([az1], [az2])
> > +AT_CLEANUP
> > +])
> >
> 
> This test also doesn't check that the MAC and networks are
> populated correctly, it could be part of the check_row_count
> filter.
Ack, Ill add MAC and network checks
> 
> 
> > +
> > +
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([ovn-ic -- Add transit router remote port])
> > +
> > +ovn_init_ic_db
> > +ovn_start az1
> > +ovn_start az2
> > +
> > +OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
> > +
> > +check ovn-ic-nbctl --wait=sb sync
> > +AT_CHECK([ovn-ic-sbctl show], [0], [dnl
> > +availability-zone az1
> > +availability-zone az2
> > +])
> > +
> > +ovn_as az2
> > +check ovn-sbctl chassis-add chassis-az2 vxlan 192.168.0.2
> > +check ovn-nbctl --wait=sb sync
> > +
> > +ovn_as az1
> > +check ovn-sbctl chassis-add chassis-az2 vxlan 192.168.0.2 \
> > +  -- set chassis chassis-az2 other_config:is-remote=true
> > +check ovn-nbctl --wait=sb sync
> > +
> > +ovn_as az1
> > +check ovn-ic-nbctl tr-add tr0
> > +check ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
> > 192.168.10.20/24 chassis=chassis-az2
> > +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep -E '(tr0-p0|type)'], [0],
> > [dnl
> > +logical_port        : tr0-p0
> > +type                : patch
> > +])
> >
> 
> This is not correct is it?
> The tr0-p0 is assigned to chassis-az2, so it should be
> type=remote on az1.
Ack, Yes, this should be remote
> 
> 
> > +
> > +ovn_as az2
> > +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep -E '(tr0-p0|type)'], [0],
> > [dnl
> > +logical_port        : tr0-p0
> > +type                : patch
> > +])
> > +
> > +OVN_CLEANUP_IC([az1], [az2])
> > +AT_CLEANUP
> > +])
> > +
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([ovn-ic -- Transit router delete port])
> >
> 
> The deletion and creation could be part of a single test.
Ack, Ill merge cretion and deleation
> 
> 
> > +
> > +ovn_init_ic_db
> > +ovn_start az1
> > +ovn_start az2
> > +
> > +OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
> > +
> > +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
> > +check ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
> > 192.168.10.20/24 chassis=chassis-az2
> > +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep tr0-p0], [0], [dnl
> > +logical_port        : tr0-p0
> > +])
> > +
> > +ovn_as az2
> > +OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep tr0-p0], [0], [dnl
> > +logical_port        : tr0-p0
> > +])
> > +
> > +ovn_as az1
> > +check ovn-ic-nbctl trp-del tr0-p0
> > +OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list Port_Binding | grep tr0-p0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding], [0], [dnl
> > +])
> > +
> > +OVN_CLEANUP_IC([az1], [az2])
> > +AT_CLEANUP
> > +])
> > +
> > +
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([ovn-ic -- port binding deletion upon transit router deletion])
> > +
> > +ovn_init_ic_db
> > +ovn_start az1
> > +ovn_start az2
> > +
> > +OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
> > +
> > +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
> > +check ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
> > 192.168.10.20/24 chassis=chassis-az2
> > +check ovn-ic-nbctl trp-add tr0 tr0-p1 00:00:00:11:22:00 192.168.10.10/24
> > 192.168.10.20/24
> > +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list Port_Binding | grep tr0 | wc
> > -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep tr0 | sort], [0], [dnl
> > +logical_port        : tr0-p0
> > +logical_port        : tr0-p1
> > +])
> > +
> > +ovn_as az2
> > +OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list Port_Binding | grep tr0| wc -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding | grep tr0 | sort], [0], [dnl
> > +logical_port        : tr0-p0
> > +logical_port        : tr0-p1
> > +])
> > +
> > +ovn_as az1
> > +check ovn-ic-nbctl tr-del tr0
> > +OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list Port_Binding | grep tr0| wc -l`])
> > +AT_CHECK([ovn-sbctl list Port_Binding], [0], [dnl
> > +])
> > +
> > +AT_CHECK([ovn-ic-nbctl list Transit_Router], [0], [dnl
> > +])
> > +AT_CHECK([ovn-ic-nbctl list Transit_Router_Port], [0], [dnl
> > +])
> > +
> >  OVN_CLEANUP_IC([az1], [az2])
> >  AT_CLEANUP
> >  ])
> > diff --git a/utilities/ovn-ic-nbctl.c b/utilities/ovn-ic-nbctl.c
> > index 5819192fe..740c75bf1 100644
> > --- a/utilities/ovn-ic-nbctl.c
> > +++ b/utilities/ovn-ic-nbctl.c
> > @@ -337,6 +337,14 @@ 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\
> > +  trp-add ROUTER PORT MAC [NETWORK]...[chassis=CHASSIS]\n\
> > +                             add a transit router PORT\n\
> > +  trp-del PORT               delete a transit router PORT\n\
> > +\n\
> >  Connection commands:\n\
> >    get-connection             print the connections\n\
> >    del-connection             delete the connections\n\
> > @@ -397,10 +405,20 @@ 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_nb_ic_uuid, NULL, NULL},
> >       {NULL, NULL, NULL}},
> >
> > -    {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}},
> > +    {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}}
> >  };
> >
> >  static void
> > @@ -507,6 +525,228 @@ ic_nbctl_ts_list(struct ctl_context *ctx)
> >      smap_destroy(&switches);
> >      free(nodes);
> >  }
> > +
> > +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;
> > +    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 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_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;
> > +
> > +    char *error = tr_by_name_or_uuid(ctx, id, must_exist, &tr);
> > +    if (error) {
> > +        ctx->error = error;
> > +        return;
> > +    }
> > +
> > +    if (!tr) {
> > +        return;
> > +    }
> > +
> > +    icnbrec_transit_router_delete(tr);
> > +}
> > +
> > +static void
> > +ic_nbctl_trp_del(struct ctl_context *ctx)
> > +{
> > +    const char *port = ctx->argv[1];
> > +    const struct icnbrec_transit_router_port *trp = NULL;
> > +
> > +    const struct icnbrec_transit_router_port *iter;
> > +
> > +    ICNBREC_TRANSIT_ROUTER_PORT_FOR_EACH (iter, ctx->idl) {
> > +        if (!strcmp(iter->name, port)) {
> > +            trp = iter;
> > +            break;
> > +        }
> > +
> > +    }
> > +
> > +    if (!trp) {
> > +        ctx->error = xasprintf("%s: router port not found", port);
> > +        return;
> > +    }
> > +
> > +    icnbrec_transit_router_port_delete(trp);
> > +}
> > +
> > +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
> > +ic_nbctl_trp_add(struct ctl_context *ctx)
> > +{
> >
> 
> trp-add supports --may-exist option, but I don't
> see any check in the function.
Ack, include changes for the --may-exist check
> 
> 
> > +    const char *tr_name = ctx->argv[1];
> > +    const char *trp_name = ctx->argv[2];
> > +    const char *mac = ctx->argv[3];
> > +    const char **networks = (const char **) &ctx->argv[4];
> > +    bool found = false;
> > +    const struct icnbrec_transit_router *tr;
> > +    ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->idl) {
> > +        if (!strcmp(tr->name, tr_name)) {
> > +            found = true;
> > +            break;
> > +        }
> > +
> > +    }
> >
> 
> Please use "tr_by_name_or_uuid()".
Ack, Ill change to tr_by_name_or_uuid
> 
> +
> > +    if (!found) {
> > +        ctl_error(ctx, "%s: transit router does not exist.", tr_name);
> > +        return;
> > +    }
> > +
> > +    found = false;
> > +    const struct icnbrec_transit_router_port *trp;
> > +    ICNBREC_TRANSIT_ROUTER_PORT_FOR_EACH (trp, ctx->idl) {
> > +        if (!strcmp(trp->name, trp_name)) {
> > +            found = true;
> > +            break;
> > +        }
> > +
> > +    }
> > +
> > +    if (found) {
> > +        ctl_error(ctx, "%s: a transit router port with this name already "
> > +                  "exists.", trp_name);
> > +        return;
> > +
> > +    }
> >
> 
> It would be probably beneficial to also have "trp_by_name_or_uuid()".
> There is the same lookup for trp-del and it would also support
> specifying the uuid, not only the name.
> 
Ack, Ill add a trp_by_name_or_uuid
> +
> > +    struct eth_addr ea;
> > +    if (!eth_addr_from_string(mac, &ea)) {
> > +        ctl_error(ctx, "%s: MAC address is invalid.", mac);
> > +        return;
> > +    }
> > +
> > +    /* Parse networks*/
> > +    int n_networks = ctx->argc - 4;
> > +    for (int i = 4; i < ctx->argc; i++) {
> > +        if (strchr(ctx->argv[i], '=')) {
> > +            n_networks = i - 4;
> > +            break;
> > +        }
> > +
> > +    }
> > +
> > +    char **settings = (char **) &ctx->argv[n_networks + 4];
> > +    int n_settings = ctx->argc - 4 - n_networks;
> > +
> > +    for (int i = 0; i < n_networks; i++) {
> > +        ovs_be32 ipv4;
> > +        unsigned int plen;
> > +        char *error;
> > +        error = ip_parse_cidr(networks[i], &ipv4, &plen);
> > +        if (error) {
> > +            free(error);
> > +            struct in6_addr ipv6;
> > +            error = ipv6_parse_cidr(networks[i], &ipv6, &plen);
> > +            if (error) {
> > +                free(error);
> > +                ctl_error(ctx, "%s: invalid network address: %s",
> > trp_name,
> > +                          networks[i]);
> > +                return;
> > +            }
> > +
> > +        }
> > +
> > +    }
> >
> 
> Please take a look at nbctl_lrp_add, particularly the section
> with network parsing, we could utilize the same
> "lrp_network_sset()" function here too.
Ack, ill change to lrp_network_sset for parsing
> 
> +
> > +    trp = icnbrec_transit_router_port_insert(ctx->txn);
> > +    icnbrec_transit_router_port_set_transit_router(trp, tr_name);
> > +    icnbrec_transit_router_port_set_name(trp, trp_name);
> > +    icnbrec_transit_router_port_set_mac(trp, mac);
> > +    icnbrec_transit_router_port_set_nb_ic_uuid(trp, tr->header_.uuid);
> > +    icnbrec_transit_router_port_set_networks(trp, networks, n_networks);
> > +
> > +    for (int i = 0; i < n_settings; i++) {
> > +        char *error = ctl_set_column("Transit_Router_Port", &trp->header_,
> > +                               settings[i], ctx->symtab);
> > +        if (error) {
> > +            ctx->error = error;
> > +            return;
> > +        }
> > +
> > +    }
> > +}
> > +
> >  static void
> >  verify_connections(struct ctl_context *ctx)
> >  {
> > @@ -714,6 +954,15 @@ 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},
> > +
> > +    [ICNBREC_TABLE_TRANSIT_ROUTER_PORT].row_ids =
> > +        {{&icnbrec_transit_router_port_col_name, NULL, NULL},
> > +        {&icnbrec_transit_router_port_col_nb_ic_uuid, NULL, NULL},
> > +        {&icnbrec_transit_router_port_col_mac, NULL, NULL},
> > +        {&icnbrec_transit_router_port_col_transit_router, NULL, NULL}},
> >  };
> >
> >
> > @@ -1010,11 +1259,24 @@ ic_nbctl_exit(int status)
> >  static const struct ctl_command_syntax ic_nbctl_commands[] = {
> >      { "init", 0, 0, "", NULL, ic_nbctl_init, NULL, "", RW },
> >      { "sync", 0, 0, "", ic_nbctl_pre_sync, NULL, NULL, "", RO },
> > +
> >      /* transit switch commands. */
> >      { "ts-add", 1, 1, "SWITCH", NULL, ic_nbctl_ts_add, NULL,
> > "--may-exist", RW },
> >      { "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 },
> > +    { "trp-add", 4, INT_MAX,
> > +        "ROUTER PORT MAC [NETWORK]...[COLUMN[:KEY]=VALUE]...",
> > +        NULL, ic_nbctl_trp_add, NULL, "--may-exist", RW },
> > +    { "trp-del", 1, 1, "PORT", NULL, ic_nbctl_trp_del, NULL,
> > "--if-exists",
> > +        RW },
> > +
> >      /* Connection commands. */
> >      {"get-connection", 0, 0, "", pre_connection, cmd_get_connection,
> > NULL, "",
> >          RO},
> > --
> > 2.50.0
> >
> > _______________________________________________
> > dev mailing list
> > [email protected]
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
> >
> I didn't check all logic in detail as quite a bit will probably
> change and it's quite hard to track what belongs where with
> a single patch.
> 
> Thanks,
> Ales

Thank you for your review Ales, responses inline

Regards,
Mairtin

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to