[ovs-dev] [PATCH v2] [RFC] ovn-controller: Experiment with restricting access to columns.

Ben Pfaff blp at ovn.org
Thu Jun 21 15:50:45 UTC 2018


I'm probably going to miss (most of?) the OVN meeting today, so please
consider this as what I've been working on for the last few days.

On Wed, Jun 20, 2018 at 11:04:46PM -0700, Ben Pfaff wrote:
> To make ovn-controller recompute incrementally, we need accurate
> dependencies for each function that reads or writes a table.  It's
> currently difficult to be sure about these dependencies, and certainly
> difficult to maintain them over time, because there's no way to actually
> enforce them.
> 
> This commit experiments with an approach that allows for fairly
> fine-grained access control within ovn-controller to tables and columns.
> It's based on generating a new version of the IDL data structures for each
> case where we want different access control.  All of these data structures
> have the same format, but the columns that a given piece of code is not
> supposed to touch are renamed to discourage programmers from using them,
> e.g. they're given names suffixed with "__noaccess" or "__writeonly".
> (This means that there is no runtime overhead to the access control since
> it only requires a cast to convert between the regular and restricted
> versions.) In addition, when a columns is supposed to be read-only,
> functions for modifying the column are not supplied.
> 
> Type-safe indexes are supplied in the same way.
> 
> Comments?
> 
> CC: Han Zhou <zhouhan at gmail.com>
> Signed-off-by: Ben Pfaff <blp at ovn.org>
> ---
>  lib/ovsdb-idl.h                         |   2 +-
>  ovn/controller/automake.mk              |  29 +-
>  ovn/controller/bfd-ovn-sb-idl.def       |  17 +
>  ovn/controller/bfd-vswitch-idl.def      |  18 +
>  ovn/controller/bfd.c                    |  89 ++---
>  ovn/controller/bfd.h                    |  17 +-
>  ovn/controller/binding-ovn-sb-idl.def   |  27 ++
>  ovn/controller/binding-vswitch-idl.def  |  22 ++
>  ovn/controller/binding.c                | 196 ++++++-----
>  ovn/controller/binding.h                |  33 +-
>  ovn/controller/chassis-ovn-sb-idl.def   |  21 ++
>  ovn/controller/chassis-vswitch-idl.def  |  12 +
>  ovn/controller/chassis.c                |  76 +++--
>  ovn/controller/chassis.h                |  23 +-
>  ovn/controller/encaps-ovn-sb-idl.def    |  14 +
>  ovn/controller/encaps-vswitch-idl.def   |  16 +
>  ovn/controller/encaps.c                 |  90 +++--
>  ovn/controller/encaps.h                 |  19 +-
>  ovn/controller/gchassis.c               |   3 +-
>  ovn/controller/gchassis.h               |   4 +-
>  ovn/controller/lflow-ovn-sb-idl.def     |  44 +++
>  ovn/controller/lflow.c                  | 155 ++++-----
>  ovn/controller/lflow.h                  |  23 +-
>  ovn/controller/lport.c                  | 102 ------
>  ovn/controller/lport.h                  |  52 ---
>  ovn/controller/ofctrl-vswitch-idl.def   |   8 +
>  ovn/controller/ofctrl.c                 |   3 +-
>  ovn/controller/ofctrl.h                 |   5 +-
>  ovn/controller/ovn-controller.c         | 178 +++++-----
>  ovn/controller/patch-ovn-sb-idl.def     |  20 ++
>  ovn/controller/patch-vswitch-idl.def    |  21 ++
>  ovn/controller/patch.c                  | 128 +++----
>  ovn/controller/patch.h                  |  24 +-
>  ovn/controller/physical-ovn-sb-idl.def  |  30 ++
>  ovn/controller/physical-vswitch-idl.def |  18 +
>  ovn/controller/physical.c               |  97 +++---
>  ovn/controller/physical.h               |  31 +-
>  ovn/controller/pinctrl-ovn-sb-idl.def   |  30 ++
>  ovn/controller/pinctrl-vswitch-idl.def  |  16 +
>  ovn/controller/pinctrl.c                | 281 ++++++++--------
>  ovn/controller/pinctrl.h                |  31 +-
>  ovn/lib/chassis-index.c                 |   6 +-
>  ovn/lib/chassis-index.h                 |   4 +-
>  ovn/northd/ovn-northd.c                 |  12 +-
>  ovsdb/ovsdb-idlc.in                     | 567 ++++++++++++++++++++++++++++++--
>  tests/test-ovsdb.c                      |  44 +--
>  46 files changed, 1696 insertions(+), 962 deletions(-)
>  create mode 100644 ovn/controller/bfd-ovn-sb-idl.def
>  create mode 100644 ovn/controller/bfd-vswitch-idl.def
>  create mode 100644 ovn/controller/binding-ovn-sb-idl.def
>  create mode 100644 ovn/controller/binding-vswitch-idl.def
>  create mode 100644 ovn/controller/chassis-ovn-sb-idl.def
>  create mode 100644 ovn/controller/chassis-vswitch-idl.def
>  create mode 100644 ovn/controller/encaps-ovn-sb-idl.def
>  create mode 100644 ovn/controller/encaps-vswitch-idl.def
>  create mode 100644 ovn/controller/lflow-ovn-sb-idl.def
>  delete mode 100644 ovn/controller/lport.c
>  delete mode 100644 ovn/controller/lport.h
>  create mode 100644 ovn/controller/ofctrl-vswitch-idl.def
>  create mode 100644 ovn/controller/patch-ovn-sb-idl.def
>  create mode 100644 ovn/controller/patch-vswitch-idl.def
>  create mode 100644 ovn/controller/physical-ovn-sb-idl.def
>  create mode 100644 ovn/controller/physical-vswitch-idl.def
>  create mode 100644 ovn/controller/pinctrl-ovn-sb-idl.def
>  create mode 100644 ovn/controller/pinctrl-vswitch-idl.def
> 
> diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
> index ea18b22f5480..58a692188db2 100644
> --- a/lib/ovsdb-idl.h
> +++ b/lib/ovsdb-idl.h
> @@ -386,7 +386,7 @@ struct ovsdb_idl_index_column {
>  
>  /* Creating an index. */
>  struct ovsdb_idl_index *ovsdb_idl_index_create(
> -    struct ovsdb_idl *, const struct ovsdb_idl_index_column *, size_t n);
> +    struct ovsdb_idl *, const struct ovsdb_idl_index_column[], size_t n);
>  struct ovsdb_idl_index *ovsdb_idl_index_create1(
>      struct ovsdb_idl *, const struct ovsdb_idl_column *);
>  struct ovsdb_idl_index *ovsdb_idl_index_create2(
> diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
> index cd5505ca6b7c..81e5e79768ef 100644
> --- a/ovn/controller/automake.mk
> +++ b/ovn/controller/automake.mk
> @@ -12,8 +12,6 @@ ovn_controller_ovn_controller_SOURCES = \
>  	ovn/controller/gchassis.h \
>  	ovn/controller/lflow.c \
>  	ovn/controller/lflow.h \
> -	ovn/controller/lport.c \
> -	ovn/controller/lport.h \
>  	ovn/controller/ofctrl.c \
>  	ovn/controller/ofctrl.h \
>  	ovn/controller/pinctrl.c \
> @@ -28,3 +26,30 @@ ovn_controller_ovn_controller_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la
>  man_MANS += ovn/controller/ovn-controller.8
>  EXTRA_DIST += ovn/controller/ovn-controller.8.xml
>  CLEANFILES += ovn/controller/ovn-controller.8
> +
> +ovn_controller_idl_def = \
> +	ovn/controller/bfd-vswitch-idl.def \
> +	ovn/controller/bfd-ovn-sb-idl.def \
> +	ovn/controller/binding-ovn-sb-idl.def \
> +	ovn/controller/binding-vswitch-idl.def \
> +	ovn/controller/chassis-ovn-sb-idl.def \
> +	ovn/controller/chassis-vswitch-idl.def \
> +	ovn/controller/encaps-ovn-sb-idl.def \
> +	ovn/controller/encaps-vswitch-idl.def \
> +	ovn/controller/lflow-ovn-sb-idl.def \
> +	ovn/controller/ofctrl-vswitch-idl.def \
> +	ovn/controller/patch-ovn-sb-idl.def \
> +	ovn/controller/patch-vswitch-idl.def \
> +	ovn/controller/physical-ovn-sb-idl.def \
> +	ovn/controller/physical-vswitch-idl.def \
> +	ovn/controller/pinctrl-ovn-sb-idl.def \
> +	ovn/controller/pinctrl-vswitch-idl.def
> +$(ovn_controller_ovn_controller_SOURCES:.c=.$(OBJEXT)): \
> +	$(ovn_controller_idl_def:.def=.h)
> +%-vswitch-idl.h: %-vswitch-idl.def lib/vswitch-idl.ovsidl ovsdb/ovsdb-idlc.in
> +	$(AM_V_GEN)$(OVSDB_IDLC) c-idl-subset lib/vswitch-idl.ovsidl $< >$@.tmp
> +	$(AM_V_at)mv $@.tmp $@
> +%-ovn-sb-idl.h: %-ovn-sb-idl.def ovn/lib/ovn-sb-idl.ovsidl ovsdb/ovsdb-idlc.in
> +	$(AM_V_GEN)$(OVSDB_IDLC) c-idl-subset ovn/lib/ovn-sb-idl.ovsidl $< >$@.tmp
> +	$(AM_V_at)mv $@.tmp $@
> +EXTRA_DIST += $(ovn_controller_idl_def)
> diff --git a/ovn/controller/bfd-ovn-sb-idl.def b/ovn/controller/bfd-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..7a43c3e29fef
> --- /dev/null
> +++ b/ovn/controller/bfd-ovn-sb-idl.def
> @@ -0,0 +1,17 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "bfd_sbrec_",
> +    "tables": {
> +        "Datapath_Binding": {
> +            "external_ids": RO,
> +            "tunnel_key": RO,
> +        },
> +        "Port_Binding": {
> +            "chassis": RO,
> +            "type": RO}},
> +    "indexes": {
> +        "Port_Binding": [["datapath"]],
> +        "Chassis": [["name"]],
> +    }
> +}
> diff --git a/ovn/controller/bfd-vswitch-idl.def b/ovn/controller/bfd-vswitch-idl.def
> new file mode 100644
> index 000000000000..74830b0dddd4
> --- /dev/null
> +++ b/ovn/controller/bfd-vswitch-idl.def
> @@ -0,0 +1,18 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "bfd_ovsrec_",
> +    "tables": {
> +        "Interface": {
> +            "name": RO,
> +            "bfd": RW,
> +            "bfd_status": RO,
> +            "options": RO},
> +        "Port": {
> +            "name": RO,
> +            "interfaces": RO,
> +            "external_ids": RO},
> +        "Bridge": {
> +            "name": RO,
> +            "ports": RO}}
> +}
> diff --git a/ovn/controller/bfd.c b/ovn/controller/bfd.c
> index 051781f38ba8..b554f982a815 100644
> --- a/ovn/controller/bfd.c
> +++ b/ovn/controller/bfd.c
> @@ -16,7 +16,6 @@
>  #include <config.h>
>  #include "bfd.h"
>  #include "gchassis.h"
> -#include "lport.h"
>  #include "ovn-controller.h"
>  
>  #include "lib/hash.h"
> @@ -32,15 +31,11 @@ VLOG_DEFINE_THIS_MODULE(ovn_bfd);
>  void
>  bfd_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>  {
> -    /* NOTE: this assumes that binding.c has added the
> -     * ovsrec_interface table */
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
> +    bfd_ovsrec_register(ovs_idl);
>  }
>  
> -
>  static void
> -interface_set_bfd(const struct ovsrec_interface *iface, bool bfd_setting)
> +interface_set_bfd(const struct bfd_ovsrec_interface *iface, bool bfd_setting)
>  {
>      const char *new_setting = bfd_setting ? "true":"false";
>      const char *current_setting = smap_get(&iface->bfd, "enable");
> @@ -50,14 +45,14 @@ interface_set_bfd(const struct ovsrec_interface *iface, bool bfd_setting)
>          return;
>      }
>      const struct smap bfd = SMAP_CONST1(&bfd, "enable", new_setting);
> -    ovsrec_interface_verify_bfd(iface);
> -    ovsrec_interface_set_bfd(iface, &bfd);
> +    bfd_ovsrec_interface_verify_bfd(iface);
> +    bfd_ovsrec_interface_set_bfd(iface, &bfd);
>      VLOG_INFO("%s BFD on interface %s", bfd_setting ? "Enabled" : "Disabled",
>                                          iface->name);
>  }
>  
>  void
> -bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
> +bfd_calculate_active_tunnels(const struct bfd_ovsrec_bridge *br_int,
>                               struct sset *active_tunnels)
>  {
>      int i;
> @@ -68,7 +63,7 @@ bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
>      }
>  
>      for (i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> +        const struct bfd_ovsrec_port *port_rec = br_int->ports[i];
>  
>          if (!strcmp(port_rec->name, br_int->name)) {
>              continue;
> @@ -76,7 +71,7 @@ bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
>  
>          int j;
>          for (j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec;
> +            const struct bfd_ovsrec_interface *iface_rec;
>              iface_rec = port_rec->interfaces[j];
>  
>              /* Check if this is a tunnel interface. */
> @@ -107,13 +102,12 @@ struct local_datapath_node {
>  
>  static void
>  bfd_travel_gw_related_chassis(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> +    struct bfd_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
>      const struct local_datapath *dp,
>      const struct hmap *local_datapaths,
>      struct sset *bfd_chassis)
>  {
>      struct ovs_list dp_list;
> -    const struct sbrec_port_binding *pb;
>      struct sset visited_dp = SSET_INITIALIZER(&visited_dp);
>      const char *dp_key;
>      struct local_datapath_node *dp_binding;
> @@ -131,8 +125,9 @@ bfd_travel_gw_related_chassis(
>      dp_binding->dp = dp;
>      ovs_list_push_back(&dp_list, &dp_binding->node);
>  
> -    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_datapath);
> +    struct bfd_sbrec_port_binding *target
> +        = bfd_sbrec_port_binding_by_datapath_new_row(
> +            sbrec_port_binding_by_datapath);
>  
>      /* Go through whole graph to figure out all chassis which may deliver
>       * packets to gateway. */
> @@ -140,7 +135,8 @@ bfd_travel_gw_related_chassis(
>          dp = dp_binding->dp;
>          free(dp_binding);
>          for (size_t i = 0; i < dp->n_peer_dps; i++) {
> -            const struct sbrec_datapath_binding *pdp = dp->peer_dps[i];
> +            const struct bfd_sbrec_datapath_binding *pdp
> +                = bfd_sbrec_datapath_binding_get(dp->peer_dps[i]);
>              if (!pdp) {
>                  continue;
>              }
> @@ -167,9 +163,11 @@ bfd_travel_gw_related_chassis(
>                                            hmap_node);
>              ovs_list_push_back(&dp_list, &dp_binding->node);
>  
> -            sbrec_port_binding_index_set_datapath(target, pdp);
> -            SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
> -                                               sbrec_port_binding_by_datapath) {
> +            bfd_sbrec_port_binding_by_datapath_set(target, pdp);
> +
> +            const struct bfd_sbrec_port_binding *pb;
> +            BFD_SBREC_PORT_BINDING_BY_DATAPATH_FOR_EACH_EQUAL (
> +                pb, target, sbrec_port_binding_by_datapath) {
>                  if (pb->chassis) {
>                      const char *chassis_name = pb->chassis->name;
>                      if (chassis_name) {
> @@ -179,31 +177,33 @@ bfd_travel_gw_related_chassis(
>              }
>          }
>      }
> -    sbrec_port_binding_index_destroy_row(target);
> +    bfd_sbrec_port_binding_by_datapath_destroy_row(target);
>  
>      sset_destroy(&visited_dp);
>  }
>  
>  static struct ovs_list *
>  bfd_find_ha_gateway_chassis(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -    const struct sbrec_datapath_binding *datapath)
> +    struct bfd_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct bfd_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +    const struct bfd_sbrec_datapath_binding *datapath)
>  {
> -    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_datapath);
> -    sbrec_port_binding_index_set_datapath(target, datapath);
> +    struct bfd_sbrec_port_binding *target
> +        = bfd_sbrec_port_binding_by_datapath_new_row(
> +            sbrec_port_binding_by_datapath);
> +    bfd_sbrec_port_binding_by_datapath_set(target, datapath);
>  
>      struct ovs_list *ha_gateway_chassis = NULL;
> -    const struct sbrec_port_binding *pb;
> -    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
> -                                       sbrec_port_binding_by_datapath) {
> +    const struct bfd_sbrec_port_binding *pb;
> +    BFD_SBREC_PORT_BINDING_BY_DATAPATH_FOR_EACH_EQUAL (
> +        pb, target, sbrec_port_binding_by_datapath) {
>          if (strcmp(pb->type, "chassisredirect")) {
>              continue;
>          }
>  
>          struct ovs_list *gateway_chassis = gateway_chassis_get_ordered(
> -            sbrec_chassis_by_name, pb);
> +            (struct sbrec_chassis_index *) sbrec_chassis_by_name, /* XXX */
> +            (struct sbrec_port_binding *) pb);                    /* XXX */
>          if (!gateway_chassis || ovs_list_is_short(gateway_chassis)) {
>              /* We don't need BFD for non-HA chassisredirect. */
>              gateway_chassis_destroy(gateway_chassis);
> @@ -213,14 +213,14 @@ bfd_find_ha_gateway_chassis(
>          ha_gateway_chassis = gateway_chassis;
>          break;
>      }
> -    sbrec_port_binding_index_destroy_row(target);
> +    bfd_sbrec_port_binding_by_datapath_destroy_row(target);
>      return ha_gateway_chassis;
>  }
>  
>  static void
>  bfd_calculate_chassis(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> +    struct bfd_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct bfd_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
>      const struct sbrec_chassis *our_chassis,
>      const struct hmap *local_datapaths,
>      struct sset *bfd_chassis)
> @@ -238,10 +238,12 @@ bfd_calculate_chassis(
>                                           "logical-router");
>          bool our_chassis_is_gw_for_dp = false;
>          if (is_router) {
> +            const struct bfd_sbrec_datapath_binding *datapath
> +                = bfd_sbrec_datapath_binding_get(dp->datapath);
>              struct ovs_list *ha_gateway_chassis
>                  = bfd_find_ha_gateway_chassis(sbrec_chassis_by_name,
>                                                sbrec_port_binding_by_datapath,
> -                                              dp->datapath);
> +                                              datapath);
>              if (ha_gateway_chassis) {
>                  our_chassis_is_gw_for_dp = gateway_chassis_contains(
>                      ha_gateway_chassis, our_chassis);
> @@ -262,12 +264,13 @@ bfd_calculate_chassis(
>  }
>  
>  void
> -bfd_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -        struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -        const struct ovsrec_interface_table *interface_table,
> -        const struct ovsrec_bridge *br_int,
> -        const struct sbrec_chassis *chassis_rec,
> -        const struct hmap *local_datapaths)
> +bfd_run(
> +    struct bfd_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct bfd_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +    const struct bfd_ovsrec_interface_table *interface_table,
> +    const struct bfd_ovsrec_bridge *br_int,
> +    const struct sbrec_chassis *chassis_rec,
> +    const struct hmap *local_datapaths)
>  {
>  
>      if (!chassis_rec) {
> @@ -293,8 +296,8 @@ bfd_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>      }
>  
>      /* Enable or disable bfd */
> -    const struct ovsrec_interface *iface;
> -    OVSREC_INTERFACE_TABLE_FOR_EACH (iface, interface_table) {
> +    const struct bfd_ovsrec_interface *iface;
> +    BFD_OVSREC_INTERFACE_TABLE_FOR_EACH (iface, interface_table) {
>          if (sset_contains(&tunnels, iface->name)) {
>                  interface_set_bfd(
>                          iface, sset_contains(&bfd_ifaces, iface->name));
> diff --git a/ovn/controller/bfd.h b/ovn/controller/bfd.h
> index 7ea345a3e642..4effe6a5a0dc 100644
> --- a/ovn/controller/bfd.h
> +++ b/ovn/controller/bfd.h
> @@ -16,22 +16,21 @@
>  #ifndef OVN_BFD_H
>  #define OVN_BFD_H 1
>  
> +#include "ovn/controller/bfd-ovn-sb-idl.h"
> +#include "ovn/controller/bfd-vswitch-idl.h"
> +
>  struct hmap;
>  struct ovsdb_idl;
> -struct ovsdb_idl_index;
> -struct ovsrec_bridge;
> -struct ovsrec_interface_table;
> -struct sbrec_chassis;
>  struct sset;
>  
>  void bfd_register_ovs_idl(struct ovsdb_idl *);
> -void bfd_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -             struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -             const struct ovsrec_interface_table *interface_table,
> -             const struct ovsrec_bridge *br_int,
> +void bfd_run(struct bfd_sbrec_chassis_by_name *,
> +             struct bfd_sbrec_port_binding_by_datapath *,
> +             const struct bfd_ovsrec_interface_table *,
> +             const struct bfd_ovsrec_bridge *br_int,
>               const struct sbrec_chassis *chassis_rec,
>               const struct hmap *local_datapaths);
> -void  bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
> +void  bfd_calculate_active_tunnels(const struct bfd_ovsrec_bridge *br_int,
>                                     struct sset *active_tunnels);
>  
>  #endif
> diff --git a/ovn/controller/binding-ovn-sb-idl.def b/ovn/controller/binding-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..8ea698f63893
> --- /dev/null
> +++ b/ovn/controller/binding-ovn-sb-idl.def
> @@ -0,0 +1,27 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "binding_sbrec_",
> +    "tables": {
> +        "Chassis": {
> +            "name": RO,
> +            "hostname": RO
> +        },
> +        "Datapath_Binding": {
> +            "external_ids": RO,
> +            "tunnel_key": RO,
> +        },
> +        "Port_Binding": {
> +            "chassis": RW,
> +            "datapath": RO,
> +            "tunnel_key": RO,
> +            "mac": RO,
> +            "type": RO,
> +            "options": RO,
> +            "parent_port": RO}},
> +    "indexes": {
> +        "Chassis": [["name"]],
> +        "Datapath_Binding": [["tunnel_key"]],
> +        "Port_Binding": [["datapath"], ["logical_port"]],
> +    }
> +}
> diff --git a/ovn/controller/binding-vswitch-idl.def b/ovn/controller/binding-vswitch-idl.def
> new file mode 100644
> index 000000000000..56fc6bc3f478
> --- /dev/null
> +++ b/ovn/controller/binding-vswitch-idl.def
> @@ -0,0 +1,22 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "binding_ovsrec_",
> +    "tables": {
> +        "Interface": {
> +            "name": RO,
> +            "external_ids": RO,
> +            "ofport": RO,
> +            "status": RO,
> +            "options": RO},
> +        "Port": {
> +            "name": RO,
> +            "interfaces": RO,
> +            "external_ids": RO,
> +            "qos": RW},
> +        "Bridge": {
> +            "name": RO,
> +            "ports": RO},
> +        "QoS": {
> +            "type": RW}}
> +}
> diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
> index 021ecddcff77..1aefbc15ebed 100644
> --- a/ovn/controller/binding.c
> +++ b/ovn/controller/binding.c
> @@ -17,7 +17,6 @@
>  #include "binding.h"
>  #include "gchassis.h"
>  #include "lflow.h"
> -#include "lport.h"
>  
>  #include "lib/bitmap.h"
>  #include "openvswitch/poll-loop.h"
> @@ -45,31 +44,11 @@ struct qos_queue {
>  void
>  binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>  {
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_qos);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_status);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
> +    binding_ovsrec_register(ovs_idl);
>  }
>  
>  static void
> -get_local_iface_ids(const struct ovsrec_bridge *br_int,
> +get_local_iface_ids(const struct binding_ovsrec_bridge *br_int,
>                      struct shash *lport_to_iface,
>                      struct sset *local_lports,
>                      struct sset *egress_ifaces)
> @@ -77,7 +56,7 @@ get_local_iface_ids(const struct ovsrec_bridge *br_int,
>      int i;
>  
>      for (i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> +        const struct binding_ovsrec_port *port_rec = br_int->ports[i];
>          const char *iface_id;
>          int j;
>  
> @@ -86,7 +65,7 @@ get_local_iface_ids(const struct ovsrec_bridge *br_int,
>          }
>  
>          for (j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec;
> +            const struct binding_ovsrec_interface *iface_rec;
>  
>              iface_rec = port_rec->interfaces[j];
>              iface_id = smap_get(&iface_rec->external_ids, "iface-id");
> @@ -110,10 +89,10 @@ get_local_iface_ids(const struct ovsrec_bridge *br_int,
>  }
>  
>  static void
> -add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                     struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                     const struct sbrec_datapath_binding *datapath,
> +add_local_datapath__(struct binding_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +                     struct binding_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +                     struct binding_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +                     const struct binding_sbrec_datapath_binding *datapath,
>                       bool has_local_l3gateway, int depth,
>                       struct hmap *local_datapaths)
>  {
> @@ -128,7 +107,7 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
>  
>      ld = xzalloc(sizeof *ld);
>      hmap_insert(local_datapaths, &ld->hmap_node, dp_key);
> -    ld->datapath = datapath;
> +    ld->datapath = (struct sbrec_datapath_binding *) datapath; /* XXX */
>      ld->localnet_port = NULL;
>      ld->has_local_l3gateway = has_local_l3gateway;
>  
> @@ -138,20 +117,21 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
>          return;
>      }
>  
> -    struct sbrec_port_binding *target =
> -        sbrec_port_binding_index_init_row(sbrec_port_binding_by_datapath);
> -    sbrec_port_binding_index_set_datapath(target, datapath);
> +    struct binding_sbrec_port_binding *target =
> +        binding_sbrec_port_binding_by_datapath_new_row(
> +            sbrec_port_binding_by_datapath);
> +    binding_sbrec_port_binding_set_datapath(target, datapath);
>  
> -    const struct sbrec_port_binding *pb;
> -    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
> -                                       sbrec_port_binding_by_datapath) {
> +    const struct binding_sbrec_port_binding *pb;
> +    BINDING_SBREC_PORT_BINDING_BY_DATAPATH_FOR_EACH_EQUAL (
> +        pb, target, sbrec_port_binding_by_datapath) {
>          if (!strcmp(pb->type, "patch")) {
>              const char *peer_name = smap_get(&pb->options, "peer");
>              if (peer_name) {
> -                const struct sbrec_port_binding *peer;
> +                const struct binding_sbrec_port_binding *peer;
>  
> -                peer = lport_lookup_by_name(sbrec_port_binding_by_name,
> -                                            peer_name);
> +                peer = binding_sbrec_port_binding_by_logical_port_find(
> +                    sbrec_port_binding_by_name, peer_name);
>  
>                  if (peer && peer->datapath) {
>                      add_local_datapath__(sbrec_datapath_binding_by_key,
> @@ -163,21 +143,23 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
>                      ld->peer_dps = xrealloc(
>                              ld->peer_dps,
>                              ld->n_peer_dps * sizeof *ld->peer_dps);
> -                    ld->peer_dps[ld->n_peer_dps - 1] = datapath_lookup_by_key(
> -                        sbrec_datapath_binding_by_key,
> -                        peer->datapath->tunnel_key);
> +                    ld->peer_dps[ld->n_peer_dps - 1]
> +                        = (struct sbrec_datapath_binding *)
> +                          binding_sbrec_datapath_binding_by_tunnel_key_find(
> +                            sbrec_datapath_binding_by_key,
> +                            peer->datapath->tunnel_key);
>                  }
>              }
>          }
>      }
> -    sbrec_port_binding_index_destroy_row(target);
> +    binding_sbrec_port_binding_by_datapath_destroy_row(target);
>  }
>  
>  static void
> -add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                   struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                   struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                   const struct sbrec_datapath_binding *datapath,
> +add_local_datapath(struct binding_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +                   struct binding_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +                   struct binding_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +                   const struct binding_sbrec_datapath_binding *datapath,
>                     bool has_local_l3gateway, struct hmap *local_datapaths)
>  {
>      add_local_datapath__(sbrec_datapath_binding_by_key,
> @@ -187,7 +169,8 @@ add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
>  }
>  
>  static void
> -get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map)
> +get_qos_params(const struct binding_sbrec_port_binding *pb,
> +               struct hmap *queue_map)
>  {
>      uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0);
>      uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0);
> @@ -205,46 +188,46 @@ get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map)
>      node->queue_id = queue_id;
>  }
>  
> -static const struct ovsrec_qos *
> -get_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
> -             const struct ovsrec_qos_table *qos_table)
> +static const struct binding_ovsrec_qos *
> +get_noop_qos(struct binding_ovsrec_txn *binding_ovsrec_txn,
> +             const struct binding_ovsrec_qos_table *qos_table)
>  {
> -    const struct ovsrec_qos *qos;
> -    OVSREC_QOS_TABLE_FOR_EACH (qos, qos_table) {
> +    const struct binding_ovsrec_qos *qos;
> +    BINDING_OVSREC_QOS_TABLE_FOR_EACH (qos, qos_table) {
>          if (!strcmp(qos->type, "linux-noop")) {
>              return qos;
>          }
>      }
>  
> -    if (!ovs_idl_txn) {
> +    if (!binding_ovsrec_txn) {
>          return NULL;
>      }
> -    qos = ovsrec_qos_insert(ovs_idl_txn);
> -    ovsrec_qos_set_type(qos, "linux-noop");
> +    qos = binding_ovsrec_qos_insert(binding_ovsrec_txn);
> +    binding_ovsrec_qos_set_type(qos, "linux-noop");
>      return qos;
>  }
>  
>  static bool
> -set_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
> -             const struct ovsrec_port_table *port_table,
> -             const struct ovsrec_qos_table *qos_table,
> +set_noop_qos(struct binding_ovsrec_txn *binding_ovsrec_txn,
> +             const struct binding_ovsrec_port_table *port_table,
> +             const struct binding_ovsrec_qos_table *qos_table,
>               struct sset *egress_ifaces)
>  {
> -    if (!ovs_idl_txn) {
> +    if (!binding_ovsrec_txn) {
>          return false;
>      }
>  
> -    const struct ovsrec_qos *noop_qos = get_noop_qos(ovs_idl_txn, qos_table);
> +    const struct binding_ovsrec_qos *noop_qos = get_noop_qos(binding_ovsrec_txn, qos_table);
>      if (!noop_qos) {
>          return false;
>      }
>  
> -    const struct ovsrec_port *port;
> +    const struct binding_ovsrec_port *port;
>      size_t count = 0;
>  
> -    OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
> +    BINDING_OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
>          if (sset_contains(egress_ifaces, port->name)) {
> -            ovsrec_port_set_qos(port, noop_qos);
> +            binding_ovsrec_port_set_qos(port, noop_qos);
>              count++;
>          }
>          if (sset_count(egress_ifaces) == count) {
> @@ -382,7 +365,7 @@ setup_qos(const char *egress_iface, struct hmap *queue_map)
>  
>  static void
>  update_local_lport_ids(struct sset *local_lport_ids,
> -                       const struct sbrec_port_binding *binding_rec)
> +                       const struct binding_sbrec_port_binding *binding_rec)
>  {
>          char buf[16];
>          snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
> @@ -392,22 +375,22 @@ update_local_lport_ids(struct sset *local_lport_ids,
>  }
>  
>  static void
> -consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                        struct ovsdb_idl_txn *ovs_idl_txn,
> -                        struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                        struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                        struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                        struct ovsdb_idl_index *sbrec_port_binding_by_name,
> +consider_local_datapath(struct binding_sbrec_txn *binding_sbrec_txn,
> +                        struct binding_ovsrec_txn *binding_ovsrec_txn,
> +                        struct binding_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +                        struct binding_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +                        struct binding_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +                        struct binding_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
>                          const struct sset *active_tunnels,
> -                        const struct sbrec_chassis *chassis_rec,
> -                        const struct sbrec_port_binding *binding_rec,
> +                        const struct binding_sbrec_chassis *chassis_rec,
> +                        const struct binding_sbrec_port_binding *binding_rec,
>                          struct hmap *qos_map,
>                          struct hmap *local_datapaths,
>                          struct shash *lport_to_iface,
>                          struct sset *local_lports,
>                          struct sset *local_lport_ids)
>  {
> -    const struct ovsrec_interface *iface_rec
> +    const struct binding_ovsrec_interface *iface_rec
>          = shash_find_data(lport_to_iface, binding_rec->logical_port);
>      struct ovs_list *gateway_chassis = NULL;
>  
> @@ -423,7 +406,7 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                             sbrec_port_binding_by_datapath,
>                             sbrec_port_binding_by_name,
>                             binding_rec->datapath, false, local_datapaths);
> -        if (iface_rec && qos_map && ovs_idl_txn) {
> +        if (iface_rec && qos_map && binding_ovsrec_txn) {
>              get_qos_params(binding_rec, qos_map);
>          }
>          /* This port is in our chassis unless it is a localport. */
> @@ -442,13 +425,16 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                                 binding_rec->datapath, false, local_datapaths);
>          }
>      } else if (!strcmp(binding_rec->type, "chassisredirect")) {
> -        gateway_chassis = gateway_chassis_get_ordered(sbrec_chassis_by_name,
> -                                                      binding_rec);
> +        gateway_chassis = gateway_chassis_get_ordered(
> +            (struct sbrec_chassis_index *) sbrec_chassis_by_name /* XXX */,
> +            (struct sbrec_port_binding *) binding_rec /* XXX */);
>          if (gateway_chassis &&
> -            gateway_chassis_contains(gateway_chassis, chassis_rec)) {
> +            gateway_chassis_contains(gateway_chassis, (struct sbrec_chassis *) chassis_rec /* XXX */)) {
>  
>              our_chassis = gateway_chassis_is_active(
> -                gateway_chassis, chassis_rec, active_tunnels);
> +                gateway_chassis,
> +                (struct sbrec_chassis *) chassis_rec /* XXX */,
> +                active_tunnels);
>  
>              add_local_datapath(sbrec_datapath_binding_by_key,
>                                 sbrec_port_binding_by_datapath,
> @@ -481,7 +467,7 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
>          update_local_lport_ids(local_lport_ids, binding_rec);
>      }
>  
> -    if (ovnsb_idl_txn) {
> +    if (binding_sbrec_txn) {
>          const char *vif_chassis = smap_get(&binding_rec->options,
>                                             "requested-chassis");
>          bool can_bind = !vif_chassis || !vif_chassis[0]
> @@ -503,12 +489,12 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                      VLOG_INFO("%s: Claiming %s",
>                                binding_rec->logical_port, binding_rec->mac[i]);
>                  }
> -                sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
> +                binding_sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
>              }
>          } else if (binding_rec->chassis == chassis_rec) {
>              VLOG_INFO("Releasing lport %s from this chassis.",
>                        binding_rec->logical_port);
> -            sbrec_port_binding_set_chassis(binding_rec, NULL);
> +            binding_sbrec_port_binding_set_chassis(binding_rec, NULL);
>          } else if (our_chassis) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>              VLOG_INFO_RL(&rl,
> @@ -522,7 +508,7 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
>  }
>  
>  static void
> -consider_localnet_port(const struct sbrec_port_binding *binding_rec,
> +consider_localnet_port(const struct binding_sbrec_port_binding *binding_rec,
>                         struct hmap *local_datapaths)
>  {
>      struct local_datapath *ld
> @@ -542,21 +528,21 @@ consider_localnet_port(const struct sbrec_port_binding *binding_rec,
>                       binding_rec->logical_port);
>          return;
>      }
> -    ld->localnet_port = binding_rec;
> +    ld->localnet_port = (struct sbrec_port_binding *) binding_rec /* XXX */;
>  }
>  
>  void
> -binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -            struct ovsdb_idl_txn *ovs_idl_txn,
> -            struct ovsdb_idl_index *sbrec_chassis_by_name,
> -            struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -            const struct ovsrec_port_table *port_table,
> -            const struct ovsrec_qos_table *qos_table,
> -            const struct sbrec_port_binding_table *port_binding_table,
> -            const struct ovsrec_bridge *br_int,
> -            const struct sbrec_chassis *chassis_rec,
> +binding_run(struct binding_sbrec_txn *binding_sbrec_txn,
> +            struct binding_ovsrec_txn *binding_ovsrec_txn,
> +            struct binding_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +            struct binding_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +            struct binding_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +            struct binding_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +            const struct binding_ovsrec_port_table *port_table,
> +            const struct binding_ovsrec_qos_table *qos_table,
> +            const struct binding_sbrec_port_binding_table *port_binding_table,
> +            const struct binding_ovsrec_bridge *br_int,
> +            const struct binding_sbrec_chassis *chassis_rec,
>              const struct sset *active_tunnels,
>              struct hmap *local_datapaths, struct sset *local_lports,
>              struct sset *local_lport_ids)
> @@ -565,7 +551,6 @@ binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>          return;
>      }
>  
> -    const struct sbrec_port_binding *binding_rec;
>      struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
>      struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
>      struct hmap qos_map;
> @@ -579,8 +564,10 @@ binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>      /* Run through each binding record to see if it is resident on this
>       * chassis and update the binding accordingly.  This includes both
>       * directly connected logical ports and children of those ports. */
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
> -        consider_local_datapath(ovnsb_idl_txn, ovs_idl_txn,
> +    const struct binding_sbrec_port_binding *binding_rec;
> +    BINDING_SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec,
> +                                               port_binding_table) {
> +        consider_local_datapath(binding_sbrec_txn, binding_ovsrec_txn,
>                                  sbrec_chassis_by_name,
>                                  sbrec_datapath_binding_by_key,
>                                  sbrec_port_binding_by_datapath,
> @@ -595,14 +582,15 @@ binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>      /* Run through each binding record to see if it is a localnet port
>       * on local datapaths discovered from above loop, and update the
>       * corresponding local datapath accordingly. */
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
> +    BINDING_SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec,
> +                                               port_binding_table) {
>          if (!strcmp(binding_rec->type, "localnet")) {
>              consider_localnet_port(binding_rec, local_datapaths);
>          }
>      }
>  
>      if (!sset_is_empty(&egress_ifaces)
> -        && set_noop_qos(ovs_idl_txn, port_table, qos_table, &egress_ifaces)) {
> +        && set_noop_qos(binding_ovsrec_txn, port_table, qos_table, &egress_ifaces)) {
>          const char *entry;
>          SSET_FOR_EACH (entry, &egress_ifaces) {
>              setup_qos(entry, &qos_map);
> @@ -617,11 +605,11 @@ binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>  /* Returns true if the database is all cleaned up, false if more work is
>   * required. */
>  bool
> -binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> +binding_cleanup(struct binding_sbrec_txn *binding_sbrec_txn,
>                  const struct sbrec_port_binding_table *port_binding_table,
>                  const struct sbrec_chassis *chassis_rec)
>  {
> -    if (!ovnsb_idl_txn) {
> +    if (!binding_sbrec_txn) {
>          return false;
>      }
>      if (!chassis_rec) {
> @@ -638,8 +626,8 @@ binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
>      }
>  
>      if (any_changes) {
> -        ovsdb_idl_txn_add_comment(
> -            ovnsb_idl_txn,
> +        binding_sbrec_txn_add_comment(
> +            binding_sbrec_txn,
>              "ovn-controller: removing all port bindings for '%s'",
>              chassis_rec->name);
>      }
> diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
> index 837e1099f0c0..d4acf78a58aa 100644
> --- a/ovn/controller/binding.h
> +++ b/ovn/controller/binding.h
> @@ -17,35 +17,30 @@
>  #ifndef OVN_BINDING_H
>  #define OVN_BINDING_H 1
>  
> +#include "ovn/controller/binding-ovn-sb-idl.h"
> +#include "ovn/controller/binding-vswitch-idl.h"
>  #include <stdbool.h>
>  
>  struct hmap;
>  struct ovsdb_idl;
> -struct ovsdb_idl_index;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_port_table;
> -struct ovsrec_qos_table;
> -struct sbrec_chassis;
> -struct sbrec_port_binding_table;
>  struct sset;
>  
>  void binding_register_ovs_idl(struct ovsdb_idl *);
> -void binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                 struct ovsdb_idl_txn *ovs_idl_txn,
> -                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                 struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                 const struct ovsrec_port_table *,
> -                 const struct ovsrec_qos_table *,
> -                 const struct sbrec_port_binding_table *,
> -                 const struct ovsrec_bridge *br_int,
> -                 const struct sbrec_chassis *,
> +void binding_run(struct binding_sbrec_txn *,
> +                 struct binding_ovsrec_txn *,
> +                 struct binding_sbrec_chassis_by_name *,
> +                 struct binding_sbrec_datapath_binding_by_tunnel_key *,
> +                 struct binding_sbrec_port_binding_by_datapath *,
> +                 struct binding_sbrec_port_binding_by_logical_port *,
> +                 const struct binding_ovsrec_port_table *,
> +                 const struct binding_ovsrec_qos_table *,
> +                 const struct binding_sbrec_port_binding_table *,
> +                 const struct binding_ovsrec_bridge *br_int,
> +                 const struct binding_sbrec_chassis *,
>                   const struct sset *active_tunnels,
>                   struct hmap *local_datapaths,
>                   struct sset *local_lports, struct sset *local_lport_ids);
> -bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> +bool binding_cleanup(struct binding_sbrec_txn *,
>                       const struct sbrec_port_binding_table *,
>                       const struct sbrec_chassis *);
>  
> diff --git a/ovn/controller/chassis-ovn-sb-idl.def b/ovn/controller/chassis-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..95c7dba0ca33
> --- /dev/null
> +++ b/ovn/controller/chassis-ovn-sb-idl.def
> @@ -0,0 +1,21 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "chassis_sbrec_",
> +    "tables": {
> +        "Chassis": {
> +            "name": RW,
> +            "hostname": RW,
> +            "external_ids": RW,
> +            "encaps": RW
> +        },
> +        "Encap": {
> +            "type": RW,
> +            "ip": RW,
> +            "options": RW,
> +            "chassis_name": WO
> +        }},
> +    "indexes": {
> +        "Chassis": [["name"]],
> +    }
> +}
> diff --git a/ovn/controller/chassis-vswitch-idl.def b/ovn/controller/chassis-vswitch-idl.def
> new file mode 100644
> index 000000000000..c9686ee82929
> --- /dev/null
> +++ b/ovn/controller/chassis-vswitch-idl.def
> @@ -0,0 +1,12 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "chassis_ovsrec_",
> +    "tables": {
> +        "Open_vSwitch": {
> +            "external_ids": RO,
> +            "iface_types": RO},
> +        "Bridge": {
> +            "datapath_type": RO,
> +        }}
> +}
> diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
> index 797c16c3ccbe..cbc3a11d1b71 100644
> --- a/ovn/controller/chassis.c
> +++ b/ovn/controller/chassis.c
> @@ -37,11 +37,7 @@ VLOG_DEFINE_THIS_MODULE(chassis);
>  void
>  chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>  {
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_iface_types);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_datapath_type);
> +    chassis_ovsrec_register(ovs_idl);
>  }
>  
>  static const char *
> @@ -75,22 +71,22 @@ get_cms_options(const struct smap *ext_ids)
>  
>  /* Returns this chassis's Chassis record, if it is available and is currently
>   * amenable to a transaction. */
> -const struct sbrec_chassis *
> -chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -            struct ovsdb_idl_index *sbrec_chassis_by_name,
> -            const struct ovsrec_open_vswitch_table *ovs_table,
> +const struct chassis_sbrec_chassis *
> +chassis_run(struct chassis_sbrec_txn *chassis_sbrec_txn,
> +            struct chassis_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +            const struct chassis_ovsrec_open_vswitch_table *ovs_table,
>              const char *chassis_id,
> -            const struct ovsrec_bridge *br_int)
> +            const struct chassis_ovsrec_bridge *br_int)
>  {
> -    if (!ovnsb_idl_txn) {
> +    if (!chassis_sbrec_txn) {
>          return NULL;
>      }
>  
> -    const struct ovsrec_open_vswitch *cfg;
>      const char *encap_type, *encap_ip;
>      static bool inited = false;
>  
> -    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> +    const struct chassis_ovsrec_open_vswitch *cfg
> +        = chassis_ovsrec_open_vswitch_table_first(ovs_table);
>      if (!cfg) {
>          VLOG_INFO("No Open_vSwitch row defined.");
>          return NULL;
> @@ -139,13 +135,15 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>      ds_chomp(&iface_types, ',');
>      const char *iface_types_str = ds_cstr(&iface_types);
>  
> -    const struct sbrec_chassis *chassis_rec
> -        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
> +    const struct chassis_sbrec_chassis *chassis_rec
> +        = chassis_sbrec_chassis_by_name_find(sbrec_chassis_by_name,
> +                                             chassis_id);
> +
>      const char *encap_csum = smap_get_def(&cfg->external_ids,
>                                            "ovn-encap-csum", "true");
>      if (chassis_rec) {
>          if (strcmp(hostname, chassis_rec->hostname)) {
> -            sbrec_chassis_set_hostname(chassis_rec, hostname);
> +            chassis_sbrec_chassis_set_hostname(chassis_rec, hostname);
>          }
>  
>          /* Determine new values for Chassis external-ids. */
> @@ -169,8 +167,8 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>              smap_replace(&new_ids, "datapath-type", datapath_type);
>              smap_replace(&new_ids, "iface-types", iface_types_str);
>              smap_replace(&new_ids, "ovn-cms-options", cms_options);
> -            sbrec_chassis_verify_external_ids(chassis_rec);
> -            sbrec_chassis_set_external_ids(chassis_rec, &new_ids);
> +            chassis_sbrec_chassis_verify_external_ids(chassis_rec);
> +            chassis_sbrec_chassis_set_external_ids(chassis_rec, &new_ids);
>              smap_destroy(&new_ids);
>          }
>  
> @@ -209,37 +207,37 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>          }
>      }
>  
> -    ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
> -                              "ovn-controller: registering chassis '%s'",
> -                              chassis_id);
> +    chassis_sbrec_txn_add_comment(chassis_sbrec_txn,
> +                                  "ovn-controller: registering chassis '%s'",
> +                                  chassis_id);
>  
>      if (!chassis_rec) {
>          struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
>          smap_add(&ext_ids, "ovn-bridge-mappings", bridge_mappings);
>          smap_add(&ext_ids, "datapath-type", datapath_type);
>          smap_add(&ext_ids, "iface-types", iface_types_str);
> -        chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn);
> -        sbrec_chassis_set_name(chassis_rec, chassis_id);
> -        sbrec_chassis_set_hostname(chassis_rec, hostname);
> -        sbrec_chassis_set_external_ids(chassis_rec, &ext_ids);
> +        chassis_rec = chassis_sbrec_chassis_insert(chassis_sbrec_txn);
> +        chassis_sbrec_chassis_set_name(chassis_rec, chassis_id);
> +        chassis_sbrec_chassis_set_hostname(chassis_rec, hostname);
> +        chassis_sbrec_chassis_set_external_ids(chassis_rec, &ext_ids);
>          smap_destroy(&ext_ids);
>      }
>  
>      ds_destroy(&iface_types);
>      int n_encaps = count_1bits(req_tunnels);
> -    struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps);
> +    struct chassis_sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps);
>      const struct smap options = SMAP_CONST1(&options, "csum", encap_csum);
>      for (int i = 0; i < n_encaps; i++) {
>          const char *type = pop_tunnel_name(&req_tunnels);
>  
> -        encaps[i] = sbrec_encap_insert(ovnsb_idl_txn);
> +        encaps[i] = chassis_sbrec_encap_insert(chassis_sbrec_txn);
>  
> -        sbrec_encap_set_type(encaps[i], type);
> -        sbrec_encap_set_ip(encaps[i], encap_ip);
> -        sbrec_encap_set_options(encaps[i], &options);
> -        sbrec_encap_set_chassis_name(encaps[i], chassis_id);
> +        chassis_sbrec_encap_set_type(encaps[i], type);
> +        chassis_sbrec_encap_set_ip(encaps[i], encap_ip);
> +        chassis_sbrec_encap_set_options(encaps[i], &options);
> +        chassis_sbrec_encap_set_chassis_name(encaps[i], chassis_id);
>      }
> -    sbrec_chassis_set_encaps(chassis_rec, encaps, n_encaps);
> +    chassis_sbrec_chassis_set_encaps(chassis_rec, encaps, n_encaps);
>      free(encaps);
>  
>      inited = true;
> @@ -249,17 +247,17 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>  /* Returns true if the database is all cleaned up, false if more work is
>   * required. */
>  bool
> -chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                const struct sbrec_chassis *chassis_rec)
> +chassis_cleanup(struct chassis_sbrec_txn *chassis_sbrec_txn,
> +                const struct chassis_sbrec_chassis *chassis_rec)
>  {
>      if (!chassis_rec) {
>          return true;
>      }
> -    if (ovnsb_idl_txn) {
> -        ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
> -                                  "ovn-controller: unregistering chassis '%s'",
> -                                  chassis_rec->name);
> -        sbrec_chassis_delete(chassis_rec);
> +    if (chassis_sbrec_txn) {
> +        chassis_sbrec_txn_add_comment(
> +            chassis_sbrec_txn,
> +            "ovn-controller: unregistering chassis '%s'", chassis_rec->name);
> +        chassis_sbrec_chassis_delete(chassis_rec);
>      }
>      return false;
>  }
> diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h
> index 6b1c357d217d..9dc3175162a8 100644
> --- a/ovn/controller/chassis.h
> +++ b/ovn/controller/chassis.h
> @@ -16,23 +16,20 @@
>  #ifndef OVN_CHASSIS_H
>  #define OVN_CHASSIS_H 1
>  
> +#include "ovn/controller/chassis-ovn-sb-idl.h"
> +#include "ovn/controller/chassis-vswitch-idl.h"
>  #include <stdbool.h>
>  
>  struct ovsdb_idl;
> -struct ovsdb_idl_index;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_open_vswitch_table;
> -struct sbrec_chassis;
> -struct sbrec_chassis_table;
>  
>  void chassis_register_ovs_idl(struct ovsdb_idl *);
> -const struct sbrec_chassis *chassis_run(
> -    struct ovsdb_idl_txn *ovnsb_idl_txn,
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    const struct ovsrec_open_vswitch_table *,
> -    const char *chassis_id, const struct ovsrec_bridge *br_int);
> -bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                     const struct sbrec_chassis *);
> +const struct chassis_sbrec_chassis *chassis_run(
> +    struct chassis_sbrec_txn *,
> +    struct chassis_sbrec_chassis_by_name *,
> +    const struct chassis_ovsrec_open_vswitch_table *,
> +    const char *chassis_id,
> +    const struct chassis_ovsrec_bridge *br_int);
> +bool chassis_cleanup(struct chassis_sbrec_txn *,
> +                     const struct chassis_sbrec_chassis *);
>  
>  #endif /* ovn/chassis.h */
> diff --git a/ovn/controller/encaps-ovn-sb-idl.def b/ovn/controller/encaps-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..05a333bd6ff4
> --- /dev/null
> +++ b/ovn/controller/encaps-ovn-sb-idl.def
> @@ -0,0 +1,14 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "encaps_sbrec_",
> +    "tables": {
> +        "Encap": {
> +            "name": RW,
> +            "type": RW,
> +            "ip": RO,
> +            "options": RW},
> +        "Chassis": {
> +            "name": RO,
> +            "encaps": RO}}
> +}
> diff --git a/ovn/controller/encaps-vswitch-idl.def b/ovn/controller/encaps-vswitch-idl.def
> new file mode 100644
> index 000000000000..0e3d8ebf4ce9
> --- /dev/null
> +++ b/ovn/controller/encaps-vswitch-idl.def
> @@ -0,0 +1,16 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "encaps_ovsrec_",
> +    "tables": {
> +        "Interface": {
> +            "name": RW,
> +            "type": RW,
> +            "options": RW},
> +        "Port": {
> +            "name": RW,
> +            "interfaces": RW,
> +            "external_ids": RW},
> +        "Bridge": {
> +            "ports": RW}}
> +}
> diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
> index fde017586ffb..7e41d56f31a3 100644
> --- a/ovn/controller/encaps.c
> +++ b/ovn/controller/encaps.c
> @@ -29,16 +29,7 @@ VLOG_DEFINE_THIS_MODULE(encaps);
>  void
>  encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>  {
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options);
> +    encaps_ovsrec_register(ovs_idl);
>  }
>  
>  /* Enough context to create a new tunnel, using tunnel_add(). */
> @@ -50,13 +41,13 @@ struct tunnel_ctx {
>       * adding a new tunnel. */
>      struct sset port_names;
>  
> -    struct ovsdb_idl_txn *ovs_txn;
> -    const struct ovsrec_bridge *br_int;
> +    struct encaps_ovsrec_txn *ovs_txn;
> +    const struct encaps_ovsrec_bridge *br_int;
>  };
>  
>  struct chassis_node {
> -    const struct ovsrec_port *port;
> -    const struct ovsrec_bridge *bridge;
> +    const struct encaps_ovsrec_port *port;
> +    const struct encaps_ovsrec_bridge *bridge;
>  };
>  
>  static char *
> @@ -80,7 +71,7 @@ tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
>  
>  static void
>  tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
> -           const struct sbrec_encap *encap)
> +           const struct encaps_sbrec_encap *encap)
>  {
>      struct smap options = SMAP_INITIALIZER(&options);
>      smap_add(&options, "remote_ip", encap->ip);
> @@ -116,18 +107,19 @@ tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
>          goto exit;
>      }
>  
> -    struct ovsrec_interface *iface = ovsrec_interface_insert(tc->ovs_txn);
> -    ovsrec_interface_set_name(iface, port_name);
> -    ovsrec_interface_set_type(iface, encap->type);
> -    ovsrec_interface_set_options(iface, &options);
> +    struct encaps_ovsrec_interface *iface
> +        = encaps_ovsrec_interface_insert(tc->ovs_txn);
> +    encaps_ovsrec_interface_set_name(iface, port_name);
> +    encaps_ovsrec_interface_set_type(iface, encap->type);
> +    encaps_ovsrec_interface_set_options(iface, &options);
>  
> -    struct ovsrec_port *port = ovsrec_port_insert(tc->ovs_txn);
> -    ovsrec_port_set_name(port, port_name);
> -    ovsrec_port_set_interfaces(port, &iface, 1);
> +    struct encaps_ovsrec_port *port = encaps_ovsrec_port_insert(tc->ovs_txn);
> +    encaps_ovsrec_port_set_name(port, port_name);
> +    encaps_ovsrec_port_set_interfaces(port, &iface, 1);
>      const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id);
> -    ovsrec_port_set_external_ids(port, &id);
> +    encaps_ovsrec_port_set_external_ids(port, &id);
>  
> -    ovsrec_bridge_update_ports_addvalue(tc->br_int, port);
> +    encaps_ovsrec_bridge_update_ports_addvalue(tc->br_int, port);
>  
>      sset_add_and_free(&tc->port_names, port_name);
>  
> @@ -135,10 +127,10 @@ exit:
>      smap_destroy(&options);
>  }
>  
> -static struct sbrec_encap *
> -preferred_encap(const struct sbrec_chassis *chassis_rec)
> +static struct encaps_sbrec_encap *
> +preferred_encap(const struct encaps_sbrec_chassis *chassis_rec)
>  {
> -    struct sbrec_encap *best_encap = NULL;
> +    struct encaps_sbrec_encap *best_encap = NULL;
>      uint32_t best_type = 0;
>  
>      for (int i = 0; i < chassis_rec->n_encaps; i++) {
> @@ -153,19 +145,16 @@ preferred_encap(const struct sbrec_chassis *chassis_rec)
>  }
>  
>  void
> -encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -           const struct ovsrec_bridge_table *bridge_table,
> -           const struct ovsrec_bridge *br_int,
> -           const struct sbrec_chassis_table *chassis_table,
> +encaps_run(struct encaps_ovsrec_txn *ovs_idl_txn,
> +           const struct encaps_ovsrec_bridge_table *bridge_table,
> +           const struct encaps_ovsrec_bridge *br_int,
> +           const struct encaps_sbrec_chassis_table *chassis_table,
>             const char *chassis_id)
>  {
>      if (!ovs_idl_txn || !br_int) {
>          return;
>      }
>  
> -    const struct sbrec_chassis *chassis_rec;
> -    const struct ovsrec_bridge *br;
> -
>      struct tunnel_ctx tc = {
>          .chassis = SHASH_INITIALIZER(&tc.chassis),
>          .port_names = SSET_INITIALIZER(&tc.port_names),
> @@ -173,16 +162,17 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
>      };
>  
>      tc.ovs_txn = ovs_idl_txn;
> -    ovsdb_idl_txn_add_comment(tc.ovs_txn,
> -                              "ovn-controller: modifying OVS tunnels '%s'",
> -                              chassis_id);
> +    encaps_ovsrec_txn_add_comment(tc.ovs_txn,
> +                                  "ovn-controller: modifying OVS tunnels '%s'",
> +                                  chassis_id);
>  
>      /* Collect all port names into tc.port_names.
>       *
>       * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
> -    OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
> +    const struct encaps_ovsrec_bridge *br;
> +    ENCAPS_OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
>          for (size_t i = 0; i < br->n_ports; i++) {
> -            const struct ovsrec_port *port = br->ports[i];
> +            const struct encaps_ovsrec_port *port = br->ports[i];
>              sset_add(&tc.port_names, port->name);
>  
>              const char *id = smap_get(&port->external_ids, "ovn-chassis-id");
> @@ -195,16 +185,18 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
>                  } else {
>                      /* Duplicate port for ovn-chassis-id.  Arbitrarily choose
>                       * to delete this one. */
> -                    ovsrec_bridge_update_ports_delvalue(br, port);
> +                    encaps_ovsrec_bridge_update_ports_delvalue(br, port);
>                  }
>              }
>          }
>      }
>  
> -    SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
> +    const struct encaps_sbrec_chassis *chassis_rec;
> +    ENCAPS_SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
>          if (strcmp(chassis_rec->name, chassis_id)) {
>              /* Create tunnels to the other chassis. */
> -            const struct sbrec_encap *encap = preferred_encap(chassis_rec);
> +            const struct encaps_sbrec_encap *encap
> +                = preferred_encap(chassis_rec);
>              if (!encap) {
>                  VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
>                  continue;
> @@ -217,7 +209,7 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
>      struct shash_node *node, *next_node;
>      SHASH_FOR_EACH_SAFE (node, next_node, &tc.chassis) {
>          struct chassis_node *chassis = node->data;
> -        ovsrec_bridge_update_ports_delvalue(chassis->bridge, chassis->port);
> +        encaps_ovsrec_bridge_update_ports_delvalue(chassis->bridge, chassis->port);
>          shash_delete(&tc.chassis, node);
>          free(chassis);
>      }
> @@ -228,15 +220,15 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
>  /* Returns true if the database is all cleaned up, false if more work is
>   * required. */
>  bool
> -encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
> -               const struct ovsrec_bridge *br_int)
> +encaps_cleanup(struct encaps_ovsrec_txn *ovs_idl_txn,
> +               const struct encaps_ovsrec_bridge *br_int)
>  {
>      if (!br_int) {
>          return true;
>      }
>  
>      /* Delete all the OVS-created tunnels from the integration bridge. */
> -    struct ovsrec_port **ports
> +    struct encaps_ovsrec_port **ports
>          = xmalloc(sizeof *br_int->ports * br_int->n_ports);
>      size_t n = 0;
>      for (size_t i = 0; i < br_int->n_ports; i++) {
> @@ -247,10 +239,10 @@ encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
>  
>      bool any_changes = n != br_int->n_ports;
>      if (any_changes && ovs_idl_txn) {
> -        ovsdb_idl_txn_add_comment(ovs_idl_txn,
> +        encaps_ovsrec_txn_add_comment(ovs_idl_txn,
>                                    "ovn-controller: destroying tunnels");
> -        ovsrec_bridge_verify_ports(br_int);
> -        ovsrec_bridge_set_ports(br_int, ports, n);
> +        encaps_ovsrec_bridge_verify_ports(br_int);
> +        encaps_ovsrec_bridge_set_ports(br_int, ports, n);
>      }
>      free(ports);
>  
> diff --git a/ovn/controller/encaps.h b/ovn/controller/encaps.h
> index 054bdfa7804d..ca8cba3d5ecb 100644
> --- a/ovn/controller/encaps.h
> +++ b/ovn/controller/encaps.h
> @@ -18,19 +18,18 @@
>  
>  #include <stdbool.h>
>  
> +#include "ovn/controller/encaps-vswitch-idl.h"
> +#include "ovn/controller/encaps-ovn-sb-idl.h"
> +
>  struct ovsdb_idl;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_bridge_table;
> -struct sbrec_chassis_table;
>  
>  void encaps_register_ovs_idl(struct ovsdb_idl *);
> -void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -                const struct ovsrec_bridge_table *,
> -                const struct ovsrec_bridge *br_int,
> -                const struct sbrec_chassis_table *,
> +void encaps_run(struct encaps_ovsrec_txn *,
> +                const struct encaps_ovsrec_bridge_table *,
> +                const struct encaps_ovsrec_bridge *br_int,
> +                const struct encaps_sbrec_chassis_table *,
>                  const char *chassis_id);
> -bool encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
> -                    const struct ovsrec_bridge *br_int);
> +bool encaps_cleanup(struct encaps_ovsrec_txn *,
> +                    const struct encaps_ovsrec_bridge *br_int);
>  
>  #endif /* ovn/encaps.h */
> diff --git a/ovn/controller/gchassis.c b/ovn/controller/gchassis.c
> index 34b78bcc0cdd..c5f695fa3fd3 100644
> --- a/ovn/controller/gchassis.c
> +++ b/ovn/controller/gchassis.c
> @@ -16,7 +16,6 @@
>  #include <config.h>
>  
>  #include "gchassis.h"
> -#include "lport.h"
>  #include "lib/sset.h"
>  #include "openvswitch/vlog.h"
>  #include "ovn/lib/chassis-index.h"
> @@ -39,7 +38,7 @@ compare_chassis_prio_(const void *a_, const void *b_)
>  }
>  
>  struct ovs_list*
> -gateway_chassis_get_ordered(struct ovsdb_idl_index *sbrec_chassis_by_name,
> +gateway_chassis_get_ordered(struct sbrec_chassis_index *sbrec_chassis_by_name,
>                              const struct sbrec_port_binding *binding)
>  {
>      const char *redir_chassis_str;
> diff --git a/ovn/controller/gchassis.h b/ovn/controller/gchassis.h
> index 901be449195f..efd64b718bb7 100644
> --- a/ovn/controller/gchassis.h
> +++ b/ovn/controller/gchassis.h
> @@ -22,8 +22,8 @@
>  #include "openvswitch/list.h"
>  
>  struct ovsdb_idl;
> -struct ovsdb_idl_index;
>  struct sbrec_chassis;
> +struct sbrec_chassis_index;
>  struct sbrec_gateway_chassis;
>  struct sbrec_port_binding;
>  struct sset;
> @@ -44,7 +44,7 @@ struct gateway_chassis {
>  
>  /* Gets, and orders by priority/name the list of Gateway_Chassis */
>  struct ovs_list *gateway_chassis_get_ordered(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> +    struct sbrec_chassis_index *sbrec_chassis_by_name,
>      const struct sbrec_port_binding *binding);
>  
>  /* Checks if an specific chassis is contained in the gateway_chassis
> diff --git a/ovn/controller/lflow-ovn-sb-idl.def b/ovn/controller/lflow-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..8b3a5133cc45
> --- /dev/null
> +++ b/ovn/controller/lflow-ovn-sb-idl.def
> @@ -0,0 +1,44 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "lflow_sbrec_",
> +    "tables": {
> +        "Datapath_Binding": {
> +            "tunnel_key": RO,
> +            "external_ids": RO,
> +        },
> +        "Port_Binding": {
> +            "datapath": RO,
> +            "tunnel_key": RO,
> +            "chassis": RO,
> +            "type": RO},
> +        "DHCP_Options": {
> +            "name": RO,
> +            "code": RO,
> +            "type": RO,
> +        },
> +        "DHCPv6_Options": {
> +            "name": RO,
> +            "code": RO,
> +            "type": RO,
> +        },
> +        "Logical_Flow": {
> +            "pipeline": RO,
> +            "logical_datapath": RO,
> +            "table_id": RO,
> +            "actions": RO,
> +            "match": RO,
> +            "priority": RO},
> +        "MAC_Binding": {
> +            "logical_port": RO,
> +            "mac": RO,
> +            "ip": RO
> +        },
> +        "Multicast_Group": {
> +            "tunnel_key": RO}},
> +    "indexes": {
> +        "Chassis": [["name"]],
> +        "Multicast_Group": [["name", "datapath"]],
> +        "Port_Binding": [["logical_port"]],
> +    }
> +}
> diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
> index 8db81927e2c4..c99dcd0f0f6c 100644
> --- a/ovn/controller/lflow.c
> +++ b/ovn/controller/lflow.c
> @@ -17,7 +17,6 @@
>  #include "lflow.h"
>  #include "coverage.h"
>  #include "gchassis.h"
> -#include "lport.h"
>  #include "ofctrl.h"
>  #include "openvswitch/dynamic-string.h"
>  #include "openvswitch/ofp-actions.h"
> @@ -50,25 +49,25 @@ lflow_init(void)
>  }
>  
>  struct lookup_port_aux {
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name;
> -    const struct sbrec_datapath_binding *dp;
> +    struct lflow_sbrec_multicast_group_by_name_datapath *sbrec_multicast_group_by_name_datapath;
> +    struct lflow_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name;
> +    const struct lflow_sbrec_datapath_binding *dp;
>  };
>  
>  struct condition_aux {
> -    struct ovsdb_idl_index *sbrec_chassis_by_name;
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name;
> -    const struct sbrec_chassis *chassis;
> +    struct lflow_sbrec_chassis_by_name *sbrec_chassis_by_name;
> +    struct lflow_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name;
> +    const struct lflow_sbrec_chassis *chassis;
>      const struct sset *active_tunnels;
>  };
>  
>  static void consider_logical_flow(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_logical_flow *,
> +    struct lflow_sbrec_chassis_by_name *,
> +    struct lflow_sbrec_multicast_group_by_name_datapath *,
> +    struct lflow_sbrec_port_binding_by_logical_port *,
> +    const struct lflow_sbrec_logical_flow *,
>      const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *,
> +    const struct lflow_sbrec_chassis *,
>      struct hmap *dhcp_opts,
>      struct hmap *dhcpv6_opts,
>      struct hmap *nd_ra_opts,
> @@ -86,15 +85,18 @@ lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
>  {
>      const struct lookup_port_aux *aux = aux_;
>  
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name);
> +    const struct lflow_sbrec_port_binding *pb
> +        = lflow_sbrec_port_binding_by_logical_port_find(
> +            aux->sbrec_port_binding_by_name, port_name);
>      if (pb && pb->datapath == aux->dp) {
>          *portp = pb->tunnel_key;
>          return true;
>      }
>  
> -    const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
> -        aux->sbrec_multicast_group_by_name_datapath, aux->dp, port_name);
> +    const struct lflow_sbrec_multicast_group *mg =
> +        lflow_sbrec_multicast_group_by_name_datapath_find(
> +            aux->sbrec_multicast_group_by_name_datapath,
> +            port_name, aux->dp);
>      if (mg) {
>          *portp = mg->tunnel_key;
>          return true;
> @@ -108,8 +110,9 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
>  {
>      const struct condition_aux *c_aux = c_aux_;
>  
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name, port_name);
> +    const struct lflow_sbrec_port_binding *pb
> +        = lflow_sbrec_port_binding_by_logical_port_find(
> +            c_aux->sbrec_port_binding_by_name, port_name);
>      if (!pb) {
>          return false;
>      }
> @@ -117,13 +120,14 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
>          /* for non-chassisredirect ports */
>          return pb->chassis && pb->chassis == c_aux->chassis;
>      } else {
> -        struct ovs_list *gateway_chassis;
> -        gateway_chassis = gateway_chassis_get_ordered(
> -            c_aux->sbrec_chassis_by_name, pb);
> +        struct ovs_list *gateway_chassis = gateway_chassis_get_ordered(
> +            (struct sbrec_chassis_index *) c_aux->sbrec_chassis_by_name /* XXX */,
> +            (struct sbrec_port_binding *) pb /* XXX */);
>          if (gateway_chassis) {
> -            bool active = gateway_chassis_is_active(gateway_chassis,
> -                                                    c_aux->chassis,
> -                                                    c_aux->active_tunnels);
> +            bool active = gateway_chassis_is_active(
> +                gateway_chassis,
> +                (struct sbrec_chassis *) c_aux->chassis /* XXX */,
> +                c_aux->active_tunnels);
>              gateway_chassis_destroy(gateway_chassis);
>              return active;
>          }
> @@ -132,23 +136,22 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
>  }
>  
>  static bool
> -is_switch(const struct sbrec_datapath_binding *ldp)
> +is_switch(const struct lflow_sbrec_datapath_binding *ldp)
>  {
>      return smap_get(&ldp->external_ids, "logical-switch") != NULL;
> -
>  }
>  
>  /* Adds the logical flows from the Logical_Flow table to flow tables. */
>  static void
>  add_logical_flows(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_dhcp_options_table *dhcp_options_table,
> -    const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
> -    const struct sbrec_logical_flow_table *logical_flow_table,
> +    struct lflow_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct lflow_sbrec_multicast_group_by_name_datapath *sbrec_multicast_group_by_name_datapath,
> +    struct lflow_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct lflow_sbrec_dhcp_options_table *dhcp_options_table,
> +    const struct lflow_sbrec_dhcpv6_options_table *dhcpv6_options_table,
> +    const struct lflow_sbrec_logical_flow_table *logical_flow_table,
>      const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *chassis,
> +    const struct lflow_sbrec_chassis *chassis,
>      const struct shash *addr_sets,
>      const struct shash *port_groups,
>      const struct sset *active_tunnels,
> @@ -158,19 +161,20 @@ add_logical_flows(
>      struct ovn_extend_table *meter_table)
>  {
>      uint32_t conj_id_ofs = 1;
> -    const struct sbrec_logical_flow *lflow;
> +    const struct lflow_sbrec_logical_flow *lflow;
>  
>      struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
>      struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
> -    const struct sbrec_dhcp_options *dhcp_opt_row;
> -    SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
> +    const struct lflow_sbrec_dhcp_options *dhcp_opt_row;
> +    LFLOW_SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row,
> +                                             dhcp_options_table) {
>          dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
>                       dhcp_opt_row->type);
>      }
>  
>  
> -    const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
> -    SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
> +    const struct lflow_sbrec_dhcpv6_options *dhcpv6_opt_row;
> +    LFLOW_SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
>                                           dhcpv6_options_table) {
>         dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
>                      dhcpv6_opt_row->type);
> @@ -179,7 +183,7 @@ add_logical_flows(
>      struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
>      nd_ra_opts_init(&nd_ra_opts);
>  
> -    SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
> +    LFLOW_SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
>          consider_logical_flow(sbrec_chassis_by_name,
>                                sbrec_multicast_group_by_name_datapath,
>                                sbrec_port_binding_by_name,
> @@ -197,12 +201,12 @@ add_logical_flows(
>  
>  static void
>  consider_logical_flow(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_logical_flow *lflow,
> +    struct lflow_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct lflow_sbrec_multicast_group_by_name_datapath *sbrec_multicast_group_by_name_datapath,
> +    struct lflow_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct lflow_sbrec_logical_flow *lflow,
>      const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *chassis,
> +    const struct lflow_sbrec_chassis *chassis,
>      struct hmap *dhcp_opts,
>      struct hmap *dhcpv6_opts,
>      struct hmap *nd_ra_opts,
> @@ -218,7 +222,7 @@ consider_logical_flow(
>      /* Determine translation of logical table IDs to physical table IDs. */
>      bool ingress = !strcmp(lflow->pipeline, "ingress");
>  
> -    const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
> +    const struct lflow_sbrec_datapath_binding *ldp = lflow->logical_datapath;
>      if (!ldp) {
>          return;
>      }
> @@ -396,12 +400,14 @@ put_load(const uint8_t *data, size_t len,
>  }
>  
>  static void
> -consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                       const struct sbrec_mac_binding *b,
> -                       struct hmap *flow_table)
> +consider_neighbor_flow(
> +    struct lflow_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct lflow_sbrec_mac_binding *b,
> +    struct hmap *flow_table)
>  {
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port);
> +    const struct lflow_sbrec_port_binding *pb
> +        = lflow_sbrec_port_binding_by_logical_port_find(
> +            sbrec_port_binding_by_name, b->logical_port);
>      if (!pb) {
>          return;
>      }
> @@ -447,12 +453,12 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>  /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
>   * southbound database. */
>  static void
> -add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                   const struct sbrec_mac_binding_table *mac_binding_table,
> +add_neighbor_flows(struct lflow_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +                   const struct lflow_sbrec_mac_binding_table *mac_binding_table,
>                     struct hmap *flow_table)
>  {
> -    const struct sbrec_mac_binding *b;
> -    SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
> +    const struct lflow_sbrec_mac_binding *b;
> +    LFLOW_SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
>          consider_neighbor_flow(sbrec_port_binding_by_name, b, flow_table);
>      }
>  }
> @@ -460,33 +466,34 @@ add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
>  /* Translates logical flows in the Logical_Flow table in the OVN_SB database
>   * into OpenFlow flows.  See ovn-architecture(7) for more information. */
>  void
> -lflow_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -          struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -          struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -          const struct sbrec_dhcp_options_table *dhcp_options_table,
> -          const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
> -          const struct sbrec_logical_flow_table *logical_flow_table,
> -          const struct sbrec_mac_binding_table *mac_binding_table,
> -          const struct sbrec_chassis *chassis,
> -          const struct hmap *local_datapaths,
> -          const struct shash *addr_sets,
> -          const struct shash *port_groups,
> -          const struct sset *active_tunnels,
> -          const struct sset *local_lport_ids,
> -          struct hmap *flow_table,
> -          struct ovn_extend_table *group_table,
> -          struct ovn_extend_table *meter_table)
> +lflow_run(
> +    struct lflow_sbrec_chassis_by_name *lflow_sbrec_chassis_by_name,
> +    struct lflow_sbrec_multicast_group_by_name_datapath *lflow_sbrec_multicast_group_by_name_datapath,
> +    struct lflow_sbrec_port_binding_by_logical_port *lflow_sbrec_port_binding_by_name,
> +    const struct lflow_sbrec_dhcp_options_table *dhcp_options_table,
> +    const struct lflow_sbrec_dhcpv6_options_table *dhcpv6_options_table,
> +    const struct lflow_sbrec_logical_flow_table *logical_flow_table,
> +    const struct lflow_sbrec_mac_binding_table *mac_binding_table,
> +    const struct lflow_sbrec_chassis *chassis,
> +    const struct hmap *local_datapaths,
> +    const struct shash *addr_sets,
> +    const struct shash *port_groups,
> +    const struct sset *active_tunnels,
> +    const struct sset *local_lport_ids,
> +    struct hmap *flow_table,
> +    struct ovn_extend_table *group_table,
> +    struct ovn_extend_table *meter_table)
>  {
>      COVERAGE_INC(lflow_run);
>  
> -    add_logical_flows(sbrec_chassis_by_name,
> -                      sbrec_multicast_group_by_name_datapath,
> -                      sbrec_port_binding_by_name, dhcp_options_table,
> +    add_logical_flows(lflow_sbrec_chassis_by_name,
> +                      lflow_sbrec_multicast_group_by_name_datapath,
> +                      lflow_sbrec_port_binding_by_name, dhcp_options_table,
>                        dhcpv6_options_table, logical_flow_table,
>                        local_datapaths, chassis, addr_sets, port_groups,
>                        active_tunnels, local_lport_ids, flow_table, group_table,
>                        meter_table);
> -    add_neighbor_flows(sbrec_port_binding_by_name, mac_binding_table,
> +    add_neighbor_flows(lflow_sbrec_port_binding_by_name, mac_binding_table,
>                         flow_table);
>  }
>  
> diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
> index d193381404e5..2799e6f2f7ee 100644
> --- a/ovn/controller/lflow.h
> +++ b/ovn/controller/lflow.h
> @@ -16,6 +16,7 @@
>  #ifndef OVN_LFLOW_H
>  #define OVN_LFLOW_H 1
>  
> +#include "ovn/controller/lflow-ovn-sb-idl.h"
>  #include "ovn/lib/logical-fields.h"
>  
>  /* Logical_Flow table translation to OpenFlow
> @@ -36,13 +37,7 @@
>  #include <stdint.h>
>  
>  struct ovn_extend_table;
> -struct ovsdb_idl_index;
>  struct hmap;
> -struct sbrec_chassis;
> -struct sbrec_dhcp_options_table;
> -struct sbrec_dhcpv6_options_table;
> -struct sbrec_logical_flow_table;
> -struct sbrec_mac_binding_table;
>  struct simap;
>  struct sset;
>  struct uuid;
> @@ -65,14 +60,14 @@ struct uuid;
>  #define LOG_PIPELINE_LEN 24
>  
>  void lflow_init(void);
> -void lflow_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -               struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -               struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -               const struct sbrec_dhcp_options_table *,
> -               const struct sbrec_dhcpv6_options_table *,
> -               const struct sbrec_logical_flow_table *,
> -               const struct sbrec_mac_binding_table *,
> -               const struct sbrec_chassis *chassis,
> +void lflow_run(struct lflow_sbrec_chassis_by_name *,
> +               struct lflow_sbrec_multicast_group_by_name_datapath *,
> +               struct lflow_sbrec_port_binding_by_logical_port *,
> +               const struct lflow_sbrec_dhcp_options_table *,
> +               const struct lflow_sbrec_dhcpv6_options_table *,
> +               const struct lflow_sbrec_logical_flow_table *,
> +               const struct lflow_sbrec_mac_binding_table *,
> +               const struct lflow_sbrec_chassis *chassis,
>                 const struct hmap *local_datapaths,
>                 const struct shash *addr_sets,
>                 const struct shash *port_groups,
> diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
> deleted file mode 100644
> index cc5c5fbb20e6..000000000000
> --- a/ovn/controller/lport.c
> +++ /dev/null
> @@ -1,102 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "lib/sset.h"
> -#include "lport.h"
> -#include "hash.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -VLOG_DEFINE_THIS_MODULE(lport);
> -
> -const struct sbrec_port_binding *
> -lport_lookup_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                     const char *name)
> -{
> -    struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_name);
> -    sbrec_port_binding_index_set_logical_port(pb, name);
> -
> -    const struct sbrec_port_binding *retval = sbrec_port_binding_index_find(
> -        sbrec_port_binding_by_name, pb);
> -
> -    sbrec_port_binding_index_destroy_row(pb);
> -
> -    return retval;
> -}
> -
> -const struct sbrec_port_binding *
> -lport_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -                    uint64_t dp_key, uint64_t port_key)
> -{
> -    /* Lookup datapath corresponding to dp_key. */
> -    const struct sbrec_datapath_binding *db = datapath_lookup_by_key(
> -        sbrec_datapath_binding_by_key, dp_key);
> -    if (!db) {
> -        return NULL;
> -    }
> -
> -    /* Build key for an indexed lookup. */
> -    struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_key);
> -    sbrec_port_binding_index_set_datapath(pb, db);
> -    sbrec_port_binding_index_set_tunnel_key(pb, port_key);
> -
> -    const struct sbrec_port_binding *retval = sbrec_port_binding_index_find(
> -        sbrec_port_binding_by_key, pb);
> -
> -    sbrec_port_binding_index_destroy_row(pb);
> -
> -    return retval;
> -}
> -
> -const struct sbrec_datapath_binding *
> -datapath_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                       uint64_t dp_key)
> -{
> -    struct sbrec_datapath_binding *db = sbrec_datapath_binding_index_init_row(
> -        sbrec_datapath_binding_by_key);
> -    sbrec_datapath_binding_index_set_tunnel_key(db, dp_key);
> -
> -    const struct sbrec_datapath_binding *retval
> -        = sbrec_datapath_binding_index_find(sbrec_datapath_binding_by_key,
> -                                            db);
> -
> -    sbrec_datapath_binding_index_destroy_row(db);
> -
> -    return retval;
> -}
> -
> -const struct sbrec_multicast_group *
> -mcgroup_lookup_by_dp_name(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    const struct sbrec_datapath_binding *db, const char *name)
> -{
> -    /* Build key for an indexed lookup. */
> -    struct sbrec_multicast_group *mc = sbrec_multicast_group_index_init_row(
> -        sbrec_multicast_group_by_name_datapath);
> -    sbrec_multicast_group_index_set_name(mc, name);
> -    sbrec_multicast_group_index_set_datapath(mc, db);
> -
> -    const struct sbrec_multicast_group *retval
> -        = sbrec_multicast_group_index_find(
> -            sbrec_multicast_group_by_name_datapath, mc);
> -
> -    sbrec_multicast_group_index_destroy_row(mc);
> -
> -    return retval;
> -}
> diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
> deleted file mode 100644
> index 7dcd5bee0aa6..000000000000
> --- a/ovn/controller/lport.h
> +++ /dev/null
> @@ -1,52 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_LPORT_H
> -#define OVN_LPORT_H 1
> -
> -#include <stdint.h>
> -
> -struct ovsdb_idl_index;
> -struct sbrec_chassis;
> -struct sbrec_datapath_binding;
> -struct sbrec_multicast_group;
> -struct sbrec_port_binding;
> -
> -
> -/* Database indexes.
> - * =================
> - *
> - * If the database IDL were a little smarter, it would allow us to directly
> - * look up data based on values of its fields.  It's not that smart (yet), so
> - * instead we define our own indexes.
> - */
> -
> -const struct sbrec_port_binding *lport_lookup_by_name(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const char *name);
> -
> -const struct sbrec_port_binding *lport_lookup_by_key(
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -    uint64_t dp_key, uint64_t port_key);
> -
> -const struct sbrec_datapath_binding *datapath_lookup_by_key(
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key, uint64_t dp_key);
> -
> -const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    const struct sbrec_datapath_binding *, const char *name);
> -
> -#endif /* ovn/lport.h */
> diff --git a/ovn/controller/ofctrl-vswitch-idl.def b/ovn/controller/ofctrl-vswitch-idl.def
> new file mode 100644
> index 000000000000..3058c122ed49
> --- /dev/null
> +++ b/ovn/controller/ofctrl-vswitch-idl.def
> @@ -0,0 +1,8 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "ofctrl_ovsrec_",
> +    "tables": {
> +        "Bridge": {
> +            "name": RO}}
> +}
> diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
> index 349de3aaa284..38ad0ec8a813 100644
> --- a/ovn/controller/ofctrl.c
> +++ b/ovn/controller/ofctrl.c
> @@ -482,7 +482,8 @@ recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type,
>   * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE.  If successful,
>   * returns the MFF_* field ID for the option, otherwise returns 0. */
>  enum mf_field_id
> -ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones)
> +ofctrl_run(const struct ofctrl_ovsrec_bridge *br_int,
> +           struct shash *pending_ct_zones)
>  {
>      char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
>      if (strcmp(target, rconn_get_target(swconn))) {
> diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
> index 886b9bd21e9a..9929adfdd5d2 100644
> --- a/ovn/controller/ofctrl.h
> +++ b/ovn/controller/ofctrl.h
> @@ -19,20 +19,19 @@
>  
>  #include <stdint.h>
>  
> +#include "ovn/controller/ofctrl-vswitch-idl.h"
>  #include "openvswitch/meta-flow.h"
> -#include "ovsdb-idl.h"
>  
>  struct ovn_extend_table;
>  struct hmap;
>  struct match;
>  struct ofpbuf;
> -struct ovsrec_bridge;
>  struct shash;
>  
>  /* Interface for OVN main loop. */
>  void ofctrl_init(struct ovn_extend_table *group_table,
>                   struct ovn_extend_table *meter_table);
> -enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int,
> +enum mf_field_id ofctrl_run(const struct ofctrl_ovsrec_bridge *br_int,
>                              struct shash *pending_ct_zones);
>  bool ofctrl_can_put(void);
>  void ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
> diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
> index 6ee72a9fafb4..23ac8b367056 100644
> --- a/ovn/controller/ovn-controller.c
> +++ b/ovn/controller/ovn-controller.c
> @@ -36,7 +36,6 @@
>  #include "openvswitch/hmap.h"
>  #include "lflow.h"
>  #include "lib/vswitch-idl.h"
> -#include "lport.h"
>  #include "ofctrl.h"
>  #include "openvswitch/vconn.h"
>  #include "openvswitch/vlog.h"
> @@ -588,25 +587,25 @@ main(int argc, char *argv[])
>          ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
>      ovsdb_idl_set_leader_only(ovnsb_idl_loop.idl, false);
>  
> -    struct ovsdb_idl_index *sbrec_chassis_by_name
> +    struct sbrec_chassis_index *sbrec_chassis_by_name
>          = chassis_index_create(ovnsb_idl_loop.idl);
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
> -        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
> -                                  &sbrec_multicast_group_col_name,
> -                                  &sbrec_multicast_group_col_datapath);
> -    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_port_binding_by_key
> -        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
> -                                  &sbrec_port_binding_col_tunnel_key,
> -                                  &sbrec_port_binding_col_datapath);
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath
> -        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> -                                  &sbrec_port_binding_col_datapath);
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key
> -        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> -                                  &sbrec_datapath_binding_col_tunnel_key);
> +    struct sbrec_multicast_group_index *sbrec_multicast_group_by_name_datapath
> +        = sbrec_multicast_group_index_create2(
> +            ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name,
> +            &sbrec_multicast_group_col_datapath);
> +    struct sbrec_port_binding_index *sbrec_port_binding_by_name
> +        = sbrec_port_binding_index_create1(
> +            ovnsb_idl_loop.idl, &sbrec_port_binding_col_logical_port);
> +    struct sbrec_port_binding_index *sbrec_port_binding_by_key
> +        = sbrec_port_binding_index_create2(ovnsb_idl_loop.idl,
> +                                           &sbrec_port_binding_col_tunnel_key,
> +                                           &sbrec_port_binding_col_datapath);
> +    struct sbrec_port_binding_index *sbrec_port_binding_by_datapath
> +        = sbrec_port_binding_index_create1(ovnsb_idl_loop.idl,
> +                                           &sbrec_port_binding_col_datapath);
> +    struct sbrec_datapath_binding_index *sbrec_datapath_binding_by_key
> +        = sbrec_datapath_binding_index_create1(
> +            ovnsb_idl_loop.idl, &sbrec_datapath_binding_col_tunnel_key);
>  
>      ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
>      update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL);
> @@ -674,21 +673,29 @@ main(int argc, char *argv[])
>  
>          const struct sbrec_chassis *chassis = NULL;
>          if (chassis_id) {
> -            chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name,
> -                                  ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
> -                                  chassis_id, br_int);
> -            encaps_run(ovs_idl_txn,
> -                       ovsrec_bridge_table_get(ovs_idl_loop.idl), br_int,
> -                       sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id);
> -            bfd_calculate_active_tunnels(br_int, &active_tunnels);
> -            binding_run(ovnsb_idl_txn, ovs_idl_txn, sbrec_chassis_by_name,
> -                        sbrec_datapath_binding_by_key,
> -                        sbrec_port_binding_by_datapath,
> -                        sbrec_port_binding_by_name,
> -                        ovsrec_port_table_get(ovs_idl_loop.idl),
> -                        ovsrec_qos_table_get(ovs_idl_loop.idl),
> -                        sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
> -                        br_int, chassis,
> +            chassis = (struct sbrec_chassis *) chassis_run(
> +                chassis_sbrec_txn_get(ovnsb_idl_txn),
> +                chassis_sbrec_chassis_by_name_get(sbrec_chassis_by_name),
> +                chassis_ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
> +                chassis_id,
> +                chassis_ovsrec_bridge_get(br_int));
> +            encaps_run(encaps_ovsrec_txn_get(ovs_idl_txn),
> +                       encaps_ovsrec_bridge_table_get(ovs_idl_loop.idl),
> +                       encaps_ovsrec_bridge_get(br_int),
> +                       encaps_sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id);
> +            bfd_calculate_active_tunnels(bfd_ovsrec_bridge_get(br_int),
> +                                         &active_tunnels);
> +            binding_run(binding_sbrec_txn_get(ovnsb_idl_txn),
> +                        binding_ovsrec_txn_get(ovs_idl_txn),
> +                        binding_sbrec_chassis_by_name_get(sbrec_chassis_by_name),
> +                        binding_sbrec_datapath_binding_by_tunnel_key_get(sbrec_datapath_binding_by_key),
> +                        binding_sbrec_port_binding_by_datapath_get(sbrec_port_binding_by_datapath),
> +                        binding_sbrec_port_binding_by_logical_port_get(sbrec_port_binding_by_name),
> +                        binding_ovsrec_port_table_get(ovs_idl_loop.idl),
> +                        binding_ovsrec_qos_table_get(ovs_idl_loop.idl),
> +                        binding_sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
> +                        binding_ovsrec_bridge_get(br_int),
> +                        binding_sbrec_chassis_get(chassis),
>                          &active_tunnels, &local_datapaths,
>                          &local_lports, &local_lport_ids);
>          }
> @@ -700,25 +707,33 @@ main(int argc, char *argv[])
>              port_groups_init(sbrec_port_group_table_get(ovnsb_idl_loop.idl),
>                               &port_groups);
>  
> -            patch_run(ovs_idl_txn,
> -                      ovsrec_bridge_table_get(ovs_idl_loop.idl),
> -                      ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
> -                      ovsrec_port_table_get(ovs_idl_loop.idl),
> -                      sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
> -                      br_int, chassis);
> -
> -            enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int,
> -                                                         &pending_ct_zones);
> -
> -            pinctrl_run(ovnsb_idl_txn, sbrec_chassis_by_name,
> -                        sbrec_datapath_binding_by_key,
> -                        sbrec_port_binding_by_datapath,
> -                        sbrec_port_binding_by_key,
> -                        sbrec_port_binding_by_name,
> -                        sbrec_dns_table_get(ovnsb_idl_loop.idl),
> -                        sbrec_mac_binding_table_get(ovnsb_idl_loop.idl),
> -                        br_int, chassis,
> -                        &local_datapaths, &active_tunnels);
> +            patch_run(patch_ovsrec_txn_get(ovs_idl_txn),
> +                      patch_ovsrec_bridge_table_get(ovs_idl_loop.idl),
> +                      patch_ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
> +                      patch_ovsrec_port_table_get(ovs_idl_loop.idl),
> +                      patch_sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
> +                      patch_ovsrec_bridge_get(br_int),
> +                      patch_sbrec_chassis_get(chassis));
> +
> +            enum mf_field_id mff_ovn_geneve = ofctrl_run(
> +                ofctrl_ovsrec_bridge_get(br_int), &pending_ct_zones);
> +
> +            pinctrl_run(
> +                pinctrl_sbrec_txn_get(ovnsb_idl_txn),
> +                pinctrl_sbrec_chassis_by_name_get(sbrec_chassis_by_name),
> +                pinctrl_sbrec_datapath_binding_by_tunnel_key_get(
> +                    sbrec_datapath_binding_by_key),
> +                pinctrl_sbrec_port_binding_by_datapath_get(
> +                    sbrec_port_binding_by_datapath),
> +                pinctrl_sbrec_port_binding_by_tunnel_key_datapath_get(
> +                    sbrec_port_binding_by_key),
> +                pinctrl_sbrec_port_binding_by_logical_port_get(
> +                    sbrec_port_binding_by_name),
> +                pinctrl_sbrec_dns_table_get(ovnsb_idl_loop.idl),
> +                pinctrl_sbrec_mac_binding_table_get(ovnsb_idl_loop.idl),
> +                pinctrl_ovsrec_bridge_get(br_int),
> +                pinctrl_sbrec_chassis_get(chassis),
> +                &local_datapaths, &active_tunnels);
>              update_ct_zones(&local_lports, &local_datapaths, &ct_zones,
>                              ct_zone_bitmap, &pending_ct_zones);
>              if (ovs_idl_txn) {
> @@ -729,31 +744,37 @@ main(int argc, char *argv[])
>                      commit_ct_zones(br_int, &pending_ct_zones);
>  
>                      struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
> -                    lflow_run(sbrec_chassis_by_name,
> -                              sbrec_multicast_group_by_name_datapath,
> -                              sbrec_port_binding_by_name,
> -                              sbrec_dhcp_options_table_get(ovnsb_idl_loop.idl),
> -                              sbrec_dhcpv6_options_table_get(ovnsb_idl_loop.idl),
> -                              sbrec_logical_flow_table_get(ovnsb_idl_loop.idl),
> -                              sbrec_mac_binding_table_get(ovnsb_idl_loop.idl),
> -                              chassis,
> -                              &local_datapaths, &addr_sets,
> -                              &port_groups, &active_tunnels, &local_lport_ids,
> -                              &flow_table, &group_table, &meter_table);
> +                    lflow_run(
> +                        lflow_sbrec_chassis_by_name_get(sbrec_chassis_by_name),
> +                        lflow_sbrec_multicast_group_by_name_datapath_get(
> +                            sbrec_multicast_group_by_name_datapath),
> +                        lflow_sbrec_port_binding_by_logical_port_get(
> +                            sbrec_port_binding_by_name),
> +                        lflow_sbrec_dhcp_options_table_get(ovnsb_idl_loop.idl),
> +                        lflow_sbrec_dhcpv6_options_table_get(ovnsb_idl_loop.idl),
> +                        lflow_sbrec_logical_flow_table_get(ovnsb_idl_loop.idl),
> +                        lflow_sbrec_mac_binding_table_get(ovnsb_idl_loop.idl),
> +                        lflow_sbrec_chassis_get(chassis),
> +                        &local_datapaths, &addr_sets,
> +                        &port_groups, &active_tunnels, &local_lport_ids,
> +                        &flow_table, &group_table, &meter_table);
>  
>                      if (chassis_id) {
> -                        bfd_run(sbrec_chassis_by_name,
> -                                sbrec_port_binding_by_datapath,
> -                                ovsrec_interface_table_get(ovs_idl_loop.idl),
> -                                br_int, chassis, &local_datapaths);
> +                        bfd_run(
> +                            bfd_sbrec_chassis_by_name_get(sbrec_chassis_by_name),
> +                            bfd_sbrec_port_binding_by_datapath_get(sbrec_port_binding_by_datapath),
> +                            bfd_ovsrec_interface_table_get(ovs_idl_loop.idl),
> +                            bfd_ovsrec_bridge_get(br_int),
> +                            chassis, &local_datapaths);
>                      }
>                      physical_run(
> -                        sbrec_chassis_by_name,
> -                        sbrec_port_binding_by_name,
> -                        sbrec_multicast_group_table_get(ovnsb_idl_loop.idl),
> -                        sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
> +                        phys_sbrec_chassis_by_name_get(sbrec_chassis_by_name),
> +                        phys_sbrec_port_binding_by_logical_port_get(sbrec_port_binding_by_name),
> +                        phys_sbrec_multicast_group_table_get(ovnsb_idl_loop.idl),
> +                        phys_sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
>                          mff_ovn_geneve,
> -                        br_int, chassis, &ct_zones,
> +                        phys_ovsrec_bridge_get(br_int),
> +                        phys_sbrec_chassis_get(chassis), &ct_zones,
>                          &local_datapaths, &local_lports,
>                          &active_tunnels,
>                          &flow_table);
> @@ -827,7 +848,7 @@ main(int argc, char *argv[])
>  
>          if (br_int) {
>              ofctrl_wait();
> -            pinctrl_wait(ovnsb_idl_txn);
> +            pinctrl_wait(pinctrl_sbrec_txn_get(ovnsb_idl_txn));
>          }
>  
>          ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
> @@ -874,9 +895,12 @@ main(int argc, char *argv[])
>  
>          /* Run all of the cleanup functions, even if one of them returns false.
>           * We're done if all of them return true. */
> -        done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis);
> -        done = chassis_cleanup(ovnsb_idl_txn, chassis) && done;
> -        done = encaps_cleanup(ovs_idl_txn, br_int) && done;
> +        done = binding_cleanup(binding_sbrec_txn_get(ovnsb_idl_txn),
> +                               port_binding_table, chassis);
> +        done = chassis_cleanup(chassis_sbrec_txn_get(ovnsb_idl_txn),
> +                               chassis_sbrec_chassis_get(chassis)) && done;
> +        done = encaps_cleanup(encaps_ovsrec_txn_get(ovs_idl_txn),
> +                              encaps_ovsrec_bridge_get(br_int)) && done;
>          if (done) {
>              poll_immediate_wake();
>          }
> diff --git a/ovn/controller/patch-ovn-sb-idl.def b/ovn/controller/patch-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..a510ee36a3c7
> --- /dev/null
> +++ b/ovn/controller/patch-ovn-sb-idl.def
> @@ -0,0 +1,20 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "patch_sbrec_",
> +    "tables": {
> +        "Chassis": {
> +            "name": RO
> +        },
> +        "Port_Binding": {
> +            "chassis": RO,
> +            "logical_port": RO,
> +            "type": RO,
> +            "options": RO,
> +        }},
> +    "indexes": {
> +        "Chassis": [["name"]],
> +        "Datapath_Binding": [["tunnel_key"]],
> +        "Port_Binding": [["tunnel_key", "datapath"], ["datapath"], ["logical_port"]],
> +    }
> +}
> diff --git a/ovn/controller/patch-vswitch-idl.def b/ovn/controller/patch-vswitch-idl.def
> new file mode 100644
> index 000000000000..7eb5e5afdf2e
> --- /dev/null
> +++ b/ovn/controller/patch-vswitch-idl.def
> @@ -0,0 +1,21 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "patch_ovsrec_",
> +    "tables": {
> +        "Interface": {
> +            "name": RW,
> +            "type": RW,
> +            "options": RW,
> +            "external_ids": RO},
> +        "Port": {
> +            "name": RW,
> +            "interfaces": RW,
> +            "external_ids": RW},
> +        "Bridge": {
> +            "name": RO,
> +            "ports": RW},
> +        "Open_vSwitch": {
> +            "external_ids": RO
> +        }}
> +}
> diff --git a/ovn/controller/patch.c b/ovn/controller/patch.c
> index a6770c6d5bd3..c0a46a65b23c 100644
> --- a/ovn/controller/patch.c
> +++ b/ovn/controller/patch.c
> @@ -20,7 +20,6 @@
>  #include "hash.h"
>  #include "lflow.h"
>  #include "lib/vswitch-idl.h"
> -#include "lport.h"
>  #include "openvswitch/hmap.h"
>  #include "openvswitch/vlog.h"
>  #include "ovn-controller.h"
> @@ -35,10 +34,10 @@ patch_port_name(const char *src, const char *dst)
>  
>  /* Return true if 'port' is a patch port with the specified 'peer'. */
>  static bool
> -match_patch_port(const struct ovsrec_port *port, const char *peer)
> +match_patch_port(const struct patch_ovsrec_port *port, const char *peer)
>  {
>      for (size_t i = 0; i < port->n_interfaces; i++) {
> -        struct ovsrec_interface *iface = port->interfaces[i];
> +        struct patch_ovsrec_interface *iface = port->interfaces[i];
>          if (strcmp(iface->type, "patch")) {
>              continue;
>          }
> @@ -56,10 +55,10 @@ match_patch_port(const struct ovsrec_port *port, const char *peer)
>   *
>   * If such a patch port already exists, removes it from 'existing_ports'. */
>  static void
> -create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn,
> +create_patch_port(struct patch_ovsrec_txn *patch_ovsrec_txn,
>                    const char *key, const char *value,
> -                  const struct ovsrec_bridge *src, const char *src_name,
> -                  const struct ovsrec_bridge *dst, const char *dst_name,
> +                  const struct patch_ovsrec_bridge *src, const char *src_name,
> +                  const struct patch_ovsrec_bridge *dst, const char *dst_name,
>                    struct shash *existing_ports)
>  {
>      for (size_t i = 0; i < src->n_ports; i++) {
> @@ -70,81 +69,96 @@ create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn,
>          }
>      }
>  
> -    ovsdb_idl_txn_add_comment(ovs_idl_txn,
> -            "ovn-controller: creating patch port '%s' from '%s' to '%s'",
> -            src_name, src->name, dst->name);
> +    patch_ovsrec_txn_add_comment(
> +        patch_ovsrec_txn,
> +        "ovn-controller: creating patch port '%s' from '%s' to '%s'",
> +        src_name, src->name, dst->name);
>  
> -    struct ovsrec_interface *iface;
> -    iface = ovsrec_interface_insert(ovs_idl_txn);
> -    ovsrec_interface_set_name(iface, src_name);
> -    ovsrec_interface_set_type(iface, "patch");
> +    struct patch_ovsrec_interface *iface;
> +    iface = patch_ovsrec_interface_insert(patch_ovsrec_txn);
> +    patch_ovsrec_interface_set_name(iface, src_name);
> +    patch_ovsrec_interface_set_type(iface, "patch");
>      const struct smap options = SMAP_CONST1(&options, "peer", dst_name);
> -    ovsrec_interface_set_options(iface, &options);
> +    patch_ovsrec_interface_set_options(iface, &options);
>  
> -    struct ovsrec_port *port;
> -    port = ovsrec_port_insert(ovs_idl_txn);
> -    ovsrec_port_set_name(port, src_name);
> -    ovsrec_port_set_interfaces(port, &iface, 1);
> +    struct patch_ovsrec_port *port;
> +    port = patch_ovsrec_port_insert(patch_ovsrec_txn);
> +    patch_ovsrec_port_set_name(port, src_name);
> +    patch_ovsrec_port_set_interfaces(port, &iface, 1);
>      const struct smap ids = SMAP_CONST1(&ids, key, value);
> -    ovsrec_port_set_external_ids(port, &ids);
> +    patch_ovsrec_port_set_external_ids(port, &ids);
>  
> -    struct ovsrec_port **ports;
> +    struct patch_ovsrec_port **ports;
>      ports = xmalloc(sizeof *ports * (src->n_ports + 1));
>      memcpy(ports, src->ports, sizeof *ports * src->n_ports);
>      ports[src->n_ports] = port;
> -    ovsrec_bridge_verify_ports(src);
> -    ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
> +    patch_ovsrec_bridge_verify_ports(src);
> +    patch_ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
>  
>      free(ports);
>  }
>  
>  static void
> -remove_port(const struct ovsrec_bridge_table *bridge_table,
> -            const struct ovsrec_port *port)
> +remove_port(const struct patch_ovsrec_bridge_table *bridge_table,
> +            const struct patch_ovsrec_port *port)
>  {
> -    const struct ovsrec_bridge *bridge;
> +    const struct patch_ovsrec_bridge *bridge;
>  
>      /* We know the port we want to delete, but we have to find the bridge its
>       * on to do so.  Note this only runs on a config change that should be
>       * pretty rare. */
> -    OVSREC_BRIDGE_TABLE_FOR_EACH (bridge, bridge_table) {
> +    PATCH_OVSREC_BRIDGE_TABLE_FOR_EACH (bridge, bridge_table) {
>          size_t i;
>          for (i = 0; i < bridge->n_ports; i++) {
>              if (bridge->ports[i] != port) {
>                  continue;
>              }
> -            struct ovsrec_port **new_ports;
> +            struct patch_ovsrec_port **new_ports;
>              new_ports = xmemdup(bridge->ports,
>                      sizeof *new_ports * (bridge->n_ports - 1));
>              if (i != bridge->n_ports - 1) {
>                  /* Removed port was not last */
>                  new_ports[i] = bridge->ports[bridge->n_ports - 1];
>              }
> -            ovsrec_bridge_verify_ports(bridge);
> -            ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
> +            patch_ovsrec_bridge_verify_ports(bridge);
> +            patch_ovsrec_bridge_set_ports(bridge, new_ports,
> +                                          bridge->n_ports - 1);
>              free(new_ports);
> -            ovsrec_port_delete(port);
> +            patch_ovsrec_port_delete(port);
>              return;
>          }
>      }
>  }
>  
> +static const struct patch_ovsrec_bridge *
> +patch_get_bridge(const struct patch_ovsrec_bridge_table *bridge_table,
> +                 const char *br_name)
> +{
> +    const struct patch_ovsrec_bridge *br;
> +    PATCH_OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
> +        if (!strcmp(br->name, br_name)) {
> +            return br;
> +        }
> +    }
> +    return NULL;
> +}
> +
>  /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for
>   * the local bridge mappings.  Removes any patch ports for bridge mappings that
>   * already existed from 'existing_ports'. */
>  static void
> -add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
> -                    const struct ovsrec_bridge_table *bridge_table,
> -                    const struct ovsrec_open_vswitch_table *ovs_table,
> -                    const struct sbrec_port_binding_table *port_binding_table,
> -                    const struct ovsrec_bridge *br_int,
> +add_bridge_mappings(struct patch_ovsrec_txn *patch_ovsrec_txn,
> +                    const struct patch_ovsrec_bridge_table *bridge_table,
> +                    const struct patch_ovsrec_open_vswitch_table *ovs_table,
> +                    const struct patch_sbrec_port_binding_table *port_binding_table,
> +                    const struct patch_ovsrec_bridge *br_int,
>                      struct shash *existing_ports,
> -                    const struct sbrec_chassis *chassis)
> +                    const struct patch_sbrec_chassis *chassis)
>  {
>      /* Get ovn-bridge-mappings. */
>      const char *mappings_cfg = "";
> -    const struct ovsrec_open_vswitch *cfg;
> -    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> +    const struct patch_ovsrec_open_vswitch *cfg;
> +    cfg = patch_ovsrec_open_vswitch_table_first(ovs_table);
>      if (cfg) {
>          mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
>          if (!mappings_cfg || !mappings_cfg[0]) {
> @@ -158,7 +172,7 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>      next = start = xstrdup(mappings_cfg);
>      while ((cur = strsep(&next, ",")) && *cur) {
>          char *network, *bridge = cur;
> -        const struct ovsrec_bridge *ovs_bridge;
> +        const struct patch_ovsrec_bridge *ovs_bridge;
>  
>          network = strsep(&bridge, ":");
>          if (!bridge || !*network || !*bridge) {
> @@ -167,7 +181,7 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>              break;
>          }
>  
> -        ovs_bridge = get_bridge(bridge_table, bridge);
> +        ovs_bridge = patch_get_bridge(bridge_table, bridge);
>          if (!ovs_bridge) {
>              VLOG_WARN("Bridge '%s' not found for network '%s'",
>                      bridge, network);
> @@ -178,8 +192,8 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>      }
>      free(start);
>  
> -    const struct sbrec_port_binding *binding;
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
> +    const struct patch_sbrec_port_binding *binding;
> +    PATCH_SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
>          const char *patch_port_id;
>          if (!strcmp(binding->type, "localnet")) {
>              patch_port_id = "ovn-localnet-port";
> @@ -203,7 +217,7 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>                           binding->type, binding->logical_port);
>              continue;
>          }
> -        struct ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings, network);
> +        struct patch_ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings, network);
>          if (!br_ln) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>              VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
> @@ -214,9 +228,11 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>  
>          char *name1 = patch_port_name(br_int->name, binding->logical_port);
>          char *name2 = patch_port_name(binding->logical_port, br_int->name);
> -        create_patch_port(ovs_idl_txn, patch_port_id, binding->logical_port,
> +        create_patch_port(patch_ovsrec_txn,
> +                          patch_port_id, binding->logical_port,
>                            br_int, name1, br_ln, name2, existing_ports);
> -        create_patch_port(ovs_idl_txn, patch_port_id, binding->logical_port,
> +        create_patch_port(patch_ovsrec_txn,
> +                          patch_port_id, binding->logical_port,
>                            br_ln, name2, br_int, name1, existing_ports);
>          free(name1);
>          free(name2);
> @@ -226,15 +242,15 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>  }
>  
>  void
> -patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -          const struct ovsrec_bridge_table *bridge_table,
> -          const struct ovsrec_open_vswitch_table *ovs_table,
> -          const struct ovsrec_port_table *port_table,
> -          const struct sbrec_port_binding_table *port_binding_table,
> -          const struct ovsrec_bridge *br_int,
> -          const struct sbrec_chassis *chassis)
> +patch_run(struct patch_ovsrec_txn *patch_ovsrec_txn,
> +          const struct patch_ovsrec_bridge_table *bridge_table,
> +          const struct patch_ovsrec_open_vswitch_table *ovs_table,
> +          const struct patch_ovsrec_port_table *port_table,
> +          const struct patch_sbrec_port_binding_table *port_binding_table,
> +          const struct patch_ovsrec_bridge *br_int,
> +          const struct patch_sbrec_chassis *chassis)
>  {
> -    if (!ovs_idl_txn) {
> +    if (!patch_ovsrec_txn) {
>          return;
>      }
>  
> @@ -245,8 +261,8 @@ patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
>       * them here, so that we delete them at the end of this function, to avoid
>       * leaving useless ports on upgrade. */
>      struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
> -    const struct ovsrec_port *port;
> -    OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
> +    const struct patch_ovsrec_port *port;
> +    PATCH_OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
>          if (smap_get(&port->external_ids, "ovn-localnet-port")
>              || smap_get(&port->external_ids, "ovn-l2gateway-port")
>              || smap_get(&port->external_ids, "ovn-l3gateway-port")
> @@ -258,7 +274,7 @@ patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
>      /* Create in the database any patch ports that should exist.  Remove from
>       * 'existing_ports' any patch ports that do exist in the database and
>       * should be there. */
> -    add_bridge_mappings(ovs_idl_txn, bridge_table, ovs_table,
> +    add_bridge_mappings(patch_ovsrec_txn, bridge_table, ovs_table,
>                          port_binding_table, br_int, &existing_ports, chassis);
>  
>      /* Now 'existing_ports' only still contains patch ports that exist in the
> diff --git a/ovn/controller/patch.h b/ovn/controller/patch.h
> index dd052cfd875f..5b45bfd8d669 100644
> --- a/ovn/controller/patch.h
> +++ b/ovn/controller/patch.h
> @@ -22,21 +22,17 @@
>   * This module adds and removes patch ports between the integration bridge and
>   * physical bridges, as directed by other-config:ovn-bridge-mappings. */
>  
> +#include "ovn/controller/patch-ovn-sb-idl.h"
> +#include "ovn/controller/patch-vswitch-idl.h"
> +
>  struct hmap;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_bridge_table;
> -struct ovsrec_open_vswitch_table;
> -struct ovsrec_port_table;
> -struct sbrec_port_binding_table;
> -struct sbrec_chassis;
>  
> -void patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -               const struct ovsrec_bridge_table *,
> -               const struct ovsrec_open_vswitch_table *,
> -               const struct ovsrec_port_table *,
> -               const struct sbrec_port_binding_table *,
> -               const struct ovsrec_bridge *br_int,
> -               const struct sbrec_chassis *);
> +void patch_run(struct patch_ovsrec_txn *,
> +               const struct patch_ovsrec_bridge_table *,
> +               const struct patch_ovsrec_open_vswitch_table *,
> +               const struct patch_ovsrec_port_table *,
> +               const struct patch_sbrec_port_binding_table *,
> +               const struct patch_ovsrec_bridge *br_int,
> +               const struct patch_sbrec_chassis *);
>  
>  #endif /* ovn/patch.h */
> diff --git a/ovn/controller/physical-ovn-sb-idl.def b/ovn/controller/physical-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..0312e744e1fa
> --- /dev/null
> +++ b/ovn/controller/physical-ovn-sb-idl.def
> @@ -0,0 +1,30 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "phys_sbrec_",
> +    "tables": {
> +        "Chassis": {
> +            "hostname": RO
> +        },
> +        "Datapath_Binding": {
> +            "tunnel_key": RO},
> +        "Port_Binding": {
> +            "logical_port": RO,
> +            "datapath": RO,
> +            "tunnel_key": RO,
> +            "chassis": RO,
> +            "type": RO,
> +            "options": RO,
> +            "parent_port": RO,
> +            "tag": RO
> +        },
> +        "Multicast_Group": {
> +            "datapath": RO,
> +            "tunnel_key": RO,
> +            "ports": RO
> +        }},
> +    "indexes": {
> +        "Port_Binding": [["logical_port"]],
> +        "Chassis": [["name"]],
> +    }
> +}
> diff --git a/ovn/controller/physical-vswitch-idl.def b/ovn/controller/physical-vswitch-idl.def
> new file mode 100644
> index 000000000000..fda2d46bb8cc
> --- /dev/null
> +++ b/ovn/controller/physical-vswitch-idl.def
> @@ -0,0 +1,18 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "phys_ovsrec_",
> +    "tables": {
> +        "Interface": {
> +            "name": RO,
> +            "type": RO,
> +            "ofport": RO,
> +            "external_ids": RO},
> +        "Port": {
> +            "name": RO,
> +            "interfaces": RO,
> +            "external_ids": RO},
> +        "Bridge": {
> +            "name": RO,
> +            "ports": RO}}
> +}
> diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
> index dcf218342632..24c25ee1bd54 100644
> --- a/ovn/controller/physical.c
> +++ b/ovn/controller/physical.c
> @@ -19,7 +19,6 @@
>  #include "flow.h"
>  #include "gchassis.h"
>  #include "lflow.h"
> -#include "lport.h"
>  #include "lib/bundle.h"
>  #include "openvswitch/poll-loop.h"
>  #include "lib/uuid.h"
> @@ -31,7 +30,6 @@
>  #include "openvswitch/ofpbuf.h"
>  #include "openvswitch/vlog.h"
>  #include "ovn-controller.h"
> -#include "ovn/lib/chassis-index.h"
>  #include "ovn/lib/ovn-sb-idl.h"
>  #include "ovn/lib/ovn-util.h"
>  #include "physical.h"
> @@ -47,18 +45,7 @@ VLOG_DEFINE_THIS_MODULE(physical);
>  void
>  physical_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>  {
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_ofport);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
> +    phys_ovsrec_register(ovs_idl);
>  }
>  
>  static struct simap localvif_to_ofport =
> @@ -125,7 +112,7 @@ put_resubmit(uint8_t table_id, struct ofpbuf *ofpacts)
>  static void
>  put_encapsulation(enum mf_field_id mff_ovn_geneve,
>                    const struct chassis_tunnel *tun,
> -                  const struct sbrec_datapath_binding *datapath,
> +                  const struct phys_sbrec_datapath_binding *datapath,
>                    uint16_t outport, struct ofpbuf *ofpacts)
>  {
>      if (tun->type == GENEVE) {
> @@ -151,12 +138,12 @@ put_stack(enum mf_field_id field, struct ofpact_stack *stack)
>      stack->subfield.n_bits = stack->subfield.field->n_bits;
>  }
>  
> -static const struct sbrec_port_binding *
> +static const struct phys_sbrec_port_binding *
>  get_localnet_port(const struct hmap *local_datapaths, int64_t tunnel_key)
>  {
>      const struct local_datapath *ld = get_local_datapath(local_datapaths,
>                                                           tunnel_key);
> -    return ld ? ld->localnet_port : NULL;
> +    return ld ? phys_sbrec_port_binding_get(ld->localnet_port) : NULL;
>  }
>  
>  /* Datapath zone IDs for connection tracking and NAT */
> @@ -167,7 +154,7 @@ struct zone_ids {
>  };
>  
>  static struct zone_ids
> -get_zone_ids(const struct sbrec_port_binding *binding,
> +get_zone_ids(const struct phys_sbrec_port_binding *binding,
>               const struct simap *ct_zones)
>  {
>      struct zone_ids zone_ids;
> @@ -267,7 +254,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
>  }
>  
>  static void
> -load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
> +load_logical_ingress_metadata(const struct phys_sbrec_port_binding *binding,
>                                const struct zone_ids *zone_ids,
>                                struct ofpbuf *ofpacts_p)
>  {
> @@ -291,14 +278,14 @@ load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
>  }
>  
>  static void
> -consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                      struct ovsdb_idl_index *sbrec_port_binding_by_name,
> +consider_port_binding(struct phys_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +                      struct phys_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
>                        enum mf_field_id mff_ovn_geneve,
>                        const struct simap *ct_zones,
>                        const struct sset *active_tunnels,
>                        const struct hmap *local_datapaths,
> -                      const struct sbrec_port_binding *binding,
> -                      const struct sbrec_chassis *chassis,
> +                      const struct phys_sbrec_port_binding *binding,
> +                      const struct phys_sbrec_chassis *chassis,
>                        struct ofpbuf *ofpacts_p,
>                        struct hmap *flow_table)
>  {
> @@ -317,8 +304,9 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
>              return;
>          }
>  
> -        const struct sbrec_port_binding *peer = lport_lookup_by_name(
> -            sbrec_port_binding_by_name, peer_name);
> +        const struct phys_sbrec_port_binding *peer
> +            = phys_sbrec_port_binding_by_logical_port_find(
> +                sbrec_port_binding_by_name, peer_name);
>          if (!peer || strcmp(peer->type, binding->type)) {
>              return;
>          }
> @@ -360,13 +348,15 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
>          return;
>      }
>  
> -    struct ovs_list *gateway_chassis
> -        = gateway_chassis_get_ordered(sbrec_chassis_by_name, binding);
> +    struct ovs_list *gateway_chassis = gateway_chassis_get_ordered(
> +        (struct sbrec_chassis_index *) sbrec_chassis_by_name /* XXX */,
> +        (struct sbrec_port_binding *) binding /* XXX */);
>  
>      if (!strcmp(binding->type, "chassisredirect")
>          && (binding->chassis == chassis
> -            || gateway_chassis_is_active(gateway_chassis, chassis,
> -                                         active_tunnels))) {
> +            || gateway_chassis_is_active(
> +                gateway_chassis, (struct sbrec_chassis *) chassis /* XXX */,
> +                active_tunnels))) {
>  
>          /* Table 33, priority 100.
>           * =======================
> @@ -384,9 +374,9 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
>  
>          const char *distributed_port = smap_get_def(&binding->options,
>                                                      "distributed-port", "");
> -        const struct sbrec_port_binding *distributed_binding
> -            = lport_lookup_by_name(sbrec_port_binding_by_name,
> -                                   distributed_port);
> +        const struct phys_sbrec_port_binding *distributed_binding
> +            = phys_sbrec_port_binding_by_logical_port_find(
> +                sbrec_port_binding_by_name, distributed_port);
>  
>          if (!distributed_binding) {
>              /* Packet will be dropped. */
> @@ -395,8 +385,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
>                           "port %s referred by chassisredirect port %s",
>                           distributed_port,
>                           binding->logical_port);
> -        } else if (binding->datapath !=
> -                   distributed_binding->datapath) {
> +        } else if (binding->datapath != distributed_binding->datapath) {
>              /* Packet will be dropped. */
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>              VLOG_WARN_RL(&rl,
> @@ -487,7 +476,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
>  
>      bool is_ha_remote = false;
>      const struct chassis_tunnel *tun = NULL;
> -    const struct sbrec_port_binding *localnet_port =
> +    const struct phys_sbrec_port_binding *localnet_port =
>          get_localnet_port(local_datapaths, dp_key);
>      if (!ofport) {
>          /* It is remote port, may be reached by tunnel or localnet port */
> @@ -732,8 +721,8 @@ static void
>  consider_mc_group(enum mf_field_id mff_ovn_geneve,
>                    const struct simap *ct_zones,
>                    const struct hmap *local_datapaths,
> -                  const struct sbrec_chassis *chassis,
> -                  const struct sbrec_multicast_group *mc,
> +                  const struct phys_sbrec_chassis *chassis,
> +                  const struct phys_sbrec_multicast_group *mc,
>                    struct ofpbuf *ofpacts_p,
>                    struct ofpbuf *remote_ofpacts_p,
>                    struct hmap *flow_table)
> @@ -765,7 +754,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
>      ofpbuf_clear(ofpacts_p);
>      ofpbuf_clear(remote_ofpacts_p);
>      for (size_t i = 0; i < mc->n_ports; i++) {
> -        struct sbrec_port_binding *port = mc->ports[i];
> +        struct phys_sbrec_port_binding *port = mc->ports[i];
>  
>          if (port->datapath != mc->datapath) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> @@ -868,13 +857,13 @@ update_ofports(struct simap *old, struct simap *new)
>  }
>  
>  void
> -physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -             struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -             const struct sbrec_multicast_group_table *multicast_group_table,
> -             const struct sbrec_port_binding_table *port_binding_table,
> +physical_run(struct phys_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +             struct phys_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +             const struct phys_sbrec_multicast_group_table *multicast_group_table,
> +             const struct phys_sbrec_port_binding_table *port_binding_table,
>               enum mf_field_id mff_ovn_geneve,
> -             const struct ovsrec_bridge *br_int,
> -             const struct sbrec_chassis *chassis,
> +             const struct phys_ovsrec_bridge *br_int,
> +             const struct phys_sbrec_chassis *chassis,
>               const struct simap *ct_zones,
>               const struct hmap *local_datapaths,
>               const struct sset *local_lports,
> @@ -889,7 +878,7 @@ physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>      struct simap new_tunnel_to_ofport =
>          SIMAP_INITIALIZER(&new_tunnel_to_ofport);
>      for (int i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> +        const struct phys_ovsrec_port *port_rec = br_int->ports[i];
>          if (!strcmp(port_rec->name, br_int->name)) {
>              continue;
>          }
> @@ -906,7 +895,8 @@ physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>                                          "ovn-l2gateway-port");
>  
>          for (int j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
> +            const struct phys_ovsrec_interface *iface_rec
> +                = port_rec->interfaces[j];
>  
>              /* Get OpenFlow port number. */
>              if (!iface_rec->n_ofport) {
> @@ -997,8 +987,8 @@ physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>  
>      /* Set up flows in table 0 for physical-to-logical translation and in table
>       * 64 for logical-to-physical translation. */
> -    const struct sbrec_port_binding *binding;
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
> +    const struct phys_sbrec_port_binding *binding;
> +    PHYS_SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
>          consider_port_binding(sbrec_chassis_by_name,
>                                sbrec_port_binding_by_name,
>                                mff_ovn_geneve, ct_zones,
> @@ -1008,10 +998,10 @@ physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>      }
>  
>      /* Handle output to multicast groups, in tables 32 and 33. */
> -    const struct sbrec_multicast_group *mc;
> +    const struct phys_sbrec_multicast_group *mc;
>      struct ofpbuf remote_ofpacts;
>      ofpbuf_init(&remote_ofpacts, 0);
> -    SBREC_MULTICAST_GROUP_TABLE_FOR_EACH (mc, multicast_group_table) {
> +    PHYS_SBREC_MULTICAST_GROUP_TABLE_FOR_EACH (mc, multicast_group_table) {
>          consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths, chassis,
>                            mc, &ofpacts, &remote_ofpacts, flow_table);
>      }
> @@ -1067,7 +1057,7 @@ physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>              continue;
>          }
>  
> -        SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
> +        PHYS_SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
>              struct match match = MATCH_CATCHALL_INITIALIZER;
>  
>              if (!binding->chassis ||
> @@ -1139,8 +1129,9 @@ physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>          /* Iterate over all local logical ports and insert a drop
>           * rule with higher priority for every localport in this
>           * datapath. */
> -        const struct sbrec_port_binding *pb = lport_lookup_by_name(
> -            sbrec_port_binding_by_name, localport);
> +        const struct phys_sbrec_port_binding *pb
> +            = phys_sbrec_port_binding_by_logical_port_find(
> +                sbrec_port_binding_by_name, localport);
>          if (pb && !strcmp(pb->type, "localport")) {
>              match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, pb->tunnel_key);
>              match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
> diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
> index a7a4def37bb6..d42ecc84ec93 100644
> --- a/ovn/controller/physical.h
> +++ b/ovn/controller/physical.h
> @@ -25,14 +25,12 @@
>   * two pipelines.
>   */
>  
> +#include "ovn/controller/physical-vswitch-idl.h"
> +#include "ovn/controller/physical-ovn-sb-idl.h"
>  #include "openvswitch/meta-flow.h"
>  
>  struct hmap;
> -struct ovsdb_idl_index;
> -struct ovsrec_bridge;
>  struct simap;
> -struct sbrec_multicast_group_table;
> -struct sbrec_port_binding_table;
>  struct sset;
>  
>  /* OVN Geneve option information.
> @@ -43,17 +41,18 @@ struct sset;
>  #define OVN_GENEVE_LEN 4
>  
>  void physical_register_ovs_idl(struct ovsdb_idl *);
> -void physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                  struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                  const struct sbrec_multicast_group_table *,
> -                  const struct sbrec_port_binding_table *,
> -                  enum mf_field_id mff_ovn_geneve,
> -                  const struct ovsrec_bridge *br_int,
> -                  const struct sbrec_chassis *chassis,
> -                  const struct simap *ct_zones,
> -                  const struct hmap *local_datapaths,
> -                  const struct sset *local_lports,
> -                  const struct sset *active_tunnels,
> -                  struct hmap *flow_table);
> +void physical_run(
> +    struct phys_sbrec_chassis_by_name *,
> +    struct phys_sbrec_port_binding_by_logical_port *,
> +    const struct phys_sbrec_multicast_group_table *,
> +    const struct phys_sbrec_port_binding_table *,
> +    enum mf_field_id mff_ovn_geneve,
> +    const struct phys_ovsrec_bridge *br_int,
> +    const struct phys_sbrec_chassis *chassis,
> +    const struct simap *ct_zones,
> +    const struct hmap *local_datapaths,
> +    const struct sset *local_lports,
> +    const struct sset *active_tunnels,
> +    struct hmap *flow_table);
>  
>  #endif /* ovn/physical.h */
> diff --git a/ovn/controller/pinctrl-ovn-sb-idl.def b/ovn/controller/pinctrl-ovn-sb-idl.def
> new file mode 100644
> index 000000000000..f6f1fe98c78a
> --- /dev/null
> +++ b/ovn/controller/pinctrl-ovn-sb-idl.def
> @@ -0,0 +1,30 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "pinctrl_sbrec_",
> +    "tables": {
> +        "DNS": {
> +            "datapaths": RO,
> +            "records": RO
> +        },
> +        "MAC_Binding": {
> +            "datapath": RW,
> +            "logical_port": RW,
> +            "ip": RW,
> +            "mac": RW
> +        },
> +        "Port_Binding": {
> +            "datapath": RO,
> +            "chassis": RO,
> +            "logical_port": RO,
> +            "type": RO,
> +            "options": RO,
> +            "nat_addresses": RO,
> +            "mac": RO
> +        }},
> +    "indexes": {
> +        "Chassis": [["name"]],
> +        "Datapath_Binding": [["tunnel_key"]],
> +        "Port_Binding": [["tunnel_key", "datapath"], ["datapath"], ["logical_port"]],
> +    }
> +}
> diff --git a/ovn/controller/pinctrl-vswitch-idl.def b/ovn/controller/pinctrl-vswitch-idl.def
> new file mode 100644
> index 000000000000..11386d69b5a2
> --- /dev/null
> +++ b/ovn/controller/pinctrl-vswitch-idl.def
> @@ -0,0 +1,16 @@
> +# -*- python -*-
> +
> +{
> +    "prefix": "pinctrl_ovsrec_",
> +    "tables": {
> +        "Interface": {
> +            "ofport": RO,
> +            "external_ids": RO},
> +        "Port": {
> +            "name": RO,
> +            "interfaces": RO,
> +            "external_ids": RO},
> +        "Bridge": {
> +            "name": RO,
> +            "ports": RO}}
> +}
> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
> index a0bf602cdda7..ca0d79e8e74e 100644
> --- a/ovn/controller/pinctrl.c
> +++ b/ovn/controller/pinctrl.c
> @@ -24,7 +24,6 @@
>  #include "dp-packet.h"
>  #include "flow.h"
>  #include "gchassis.h"
> -#include "lport.h"
>  #include "nx-match.h"
>  #include "ovn-controller.h"
>  #include "lib/packets.h"
> @@ -67,22 +66,22 @@ static void pinctrl_handle_put_mac_binding(const struct flow *md,
>  static void init_put_mac_bindings(void);
>  static void destroy_put_mac_bindings(void);
>  static void run_put_mac_bindings(
> -    struct ovsdb_idl_txn *ovnsb_idl_txn,
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -    const struct sbrec_mac_binding_table *);
> -static void wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn);
> +    struct pinctrl_sbrec_txn *,
> +    struct pinctrl_sbrec_datapath_binding_by_tunnel_key *,
> +    struct pinctrl_sbrec_port_binding_by_tunnel_key_datapath *,
> +    const struct pinctrl_sbrec_mac_binding_table *);
> +static void wait_put_mac_bindings(struct pinctrl_sbrec_txn *);
>  static void flush_put_mac_bindings(void);
>  
>  static void init_send_garps(void);
>  static void destroy_send_garps(void);
>  static void send_garp_wait(void);
>  static void send_garp_run(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct ovsrec_bridge *,
> -    const struct sbrec_chassis *,
> +    struct pinctrl_sbrec_chassis_by_name *,
> +    struct pinctrl_sbrec_port_binding_by_datapath *,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *,
> +    const struct pinctrl_ovsrec_bridge *,
> +    const struct pinctrl_sbrec_chassis *,
>      const struct hmap *local_datapaths,
>      const struct sset *active_tunnels);
>  static void pinctrl_handle_nd_na(const struct flow *ip_flow,
> @@ -102,8 +101,8 @@ static void init_ipv6_ras(void);
>  static void destroy_ipv6_ras(void);
>  static void ipv6_ra_wait(void);
>  static void send_ipv6_ras(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> +    struct pinctrl_sbrec_port_binding_by_datapath *,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *,
>      const struct hmap *local_datapaths);
>  ;
>  
> @@ -865,7 +864,7 @@ put_be32(struct ofpbuf *buf, ovs_be32 x)
>  
>  static void
>  pinctrl_handle_dns_lookup(
> -    const struct sbrec_dns_table *dns_table,
> +    const struct pinctrl_sbrec_dns_table *dns_table,
>      struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
>      struct ofpbuf *userdata, struct ofpbuf *continuation)
>  {
> @@ -958,9 +957,9 @@ pinctrl_handle_dns_lookup(
>      }
>  
>      uint64_t dp_key = ntohll(pin->flow_metadata.flow.metadata);
> -    const struct sbrec_dns *sbrec_dns;
> +    const struct pinctrl_sbrec_dns *sbrec_dns;
>      const char *answer_ips = NULL;
> -    SBREC_DNS_TABLE_FOR_EACH (sbrec_dns, dns_table) {
> +    PINCTRL_SBREC_DNS_TABLE_FOR_EACH (sbrec_dns, dns_table) {
>          for (size_t i = 0; i < sbrec_dns->n_datapaths; i++) {
>              if (sbrec_dns->datapaths[i]->tunnel_key == dp_key) {
>                  answer_ips = smap_get(&sbrec_dns->records,
> @@ -1119,7 +1118,7 @@ exit:
>  
>  static void
>  process_packet_in(const struct ofp_header *msg,
> -                  const struct sbrec_dns_table *dns_table)
> +                  const struct pinctrl_sbrec_dns_table *dns_table)
>  {
>      static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>  
> @@ -1218,7 +1217,7 @@ process_packet_in(const struct ofp_header *msg,
>  }
>  
>  static void
> -pinctrl_recv(const struct sbrec_dns_table *dns_table,
> +pinctrl_recv(const struct pinctrl_sbrec_dns_table *dns_table,
>               const struct ofp_header *oh, enum ofptype type)
>  {
>      if (type == OFPTYPE_ECHO_REQUEST) {
> @@ -1245,18 +1244,19 @@ pinctrl_recv(const struct sbrec_dns_table *dns_table,
>  }
>  
>  void
> -pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -            struct ovsdb_idl_index *sbrec_chassis_by_name,
> -            struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -            const struct sbrec_dns_table *dns_table,
> -            const struct sbrec_mac_binding_table *mac_binding_table,
> -            const struct ovsrec_bridge *br_int,
> -            const struct sbrec_chassis *chassis,
> -            const struct hmap *local_datapaths,
> -            const struct sset *active_tunnels)
> +pinctrl_run(
> +    struct pinctrl_sbrec_txn *pinctrl_sbrec_txn,
> +    struct pinctrl_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct pinctrl_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +    struct pinctrl_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +    struct pinctrl_sbrec_port_binding_by_tunnel_key_datapath *sbrec_port_binding_by_key,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct pinctrl_sbrec_dns_table *dns_table,
> +    const struct pinctrl_sbrec_mac_binding_table *mac_binding_table,
> +    const struct pinctrl_ovsrec_bridge *br_int,
> +    const struct pinctrl_sbrec_chassis *chassis,
> +    const struct hmap *local_datapaths,
> +    const struct sset *active_tunnels)
>  {
>      char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
>      if (strcmp(target, rconn_get_target(swconn))) {
> @@ -1292,7 +1292,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>          ofpbuf_delete(msg);
>      }
>  
> -    run_put_mac_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
> +    run_put_mac_bindings(pinctrl_sbrec_txn, sbrec_datapath_binding_by_key,
>                           sbrec_port_binding_by_key, mac_binding_table);
>      send_garp_run(sbrec_chassis_by_name, sbrec_port_binding_by_datapath,
>                    sbrec_port_binding_by_name, br_int, chassis,
> @@ -1366,7 +1366,7 @@ destroy_ipv6_ras(void)
>  }
>  
>  static struct ipv6_ra_config *
> -ipv6_ra_update_config(const struct sbrec_port_binding *pb)
> +ipv6_ra_update_config(const struct pinctrl_sbrec_port_binding *pb)
>  {
>      struct ipv6_ra_config *config;
>  
> @@ -1510,9 +1510,10 @@ ipv6_ra_wait(void)
>  }
>  
>  static void
> -send_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -              struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -              const struct hmap *local_datapaths)
> +send_ipv6_ras(
> +    struct pinctrl_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct hmap *local_datapaths)
>  {
>      struct shash_node *iter, *iter_next;
>  
> @@ -1525,13 +1526,15 @@ send_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
>  
>      const struct local_datapath *ld;
>      HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
> -        struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
> -            sbrec_port_binding_by_datapath);
> -        sbrec_port_binding_index_set_datapath(target, ld->datapath);
> -
> -        struct sbrec_port_binding *pb;
> -        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
> -                                           sbrec_port_binding_by_datapath) {
> +        struct pinctrl_sbrec_port_binding *target
> +            = pinctrl_sbrec_port_binding_by_datapath_new_row(
> +                sbrec_port_binding_by_datapath);
> +        pinctrl_sbrec_port_binding_set_datapath(
> +            target, pinctrl_sbrec_datapath_binding_get(ld->datapath));
> +
> +        struct pinctrl_sbrec_port_binding *pb;
> +        PINCTRL_SBREC_PORT_BINDING_BY_DATAPATH_FOR_EACH_EQUAL (
> +            pb, target, sbrec_port_binding_by_datapath) {
>              if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) {
>                  continue;
>              }
> @@ -1541,8 +1544,9 @@ send_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
>                  continue;
>              }
>  
> -            const struct sbrec_port_binding *peer
> -                = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
> +            const struct pinctrl_sbrec_port_binding *peer
> +                = pinctrl_sbrec_port_binding_by_logical_port_find(
> +                    sbrec_port_binding_by_name, peer_s);
>              if (!peer) {
>                  continue;
>              }
> @@ -1579,7 +1583,7 @@ send_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
>                  send_ipv6_ra_time = next_ra;
>              }
>          }
> -        sbrec_port_binding_index_destroy_row(target);
> +        pinctrl_sbrec_port_binding_by_datapath_destroy_row(target);
>      }
>  
>      /* Remove those that are no longer in the SB database */
> @@ -1593,9 +1597,9 @@ send_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
>  }
>  
>  void
> -pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn)
> +pinctrl_wait(struct pinctrl_sbrec_txn *pinctrl_sbrec_txn)
>  {
> -    wait_put_mac_bindings(ovnsb_idl_txn);
> +    wait_put_mac_bindings(pinctrl_sbrec_txn);
>      rconn_run_wait(swconn);
>      rconn_recv_wait(swconn);
>      send_garp_wait();
> @@ -1617,8 +1621,8 @@ pinctrl_destroy(void)
>   * updating the MAC_Binding table in the southbound database.
>   *
>   * This code could be a lot simpler if the database could always be updated,
> - * but in fact we can only update it when 'ovnsb_idl_txn' is nonnull.  Thus,
> - * we buffer up a few put_mac_bindings (but we don't keep them longer
> + * but in fact we can only update it when 'pinctrl_sbrec_txn' is nonnull.
> + * Thus, we buffer up a few put_mac_bindings (but we don't keep them longer
>   * than 1 second) and apply them whenever a database transaction is
>   * available. */
>  
> @@ -1703,20 +1707,25 @@ pinctrl_handle_put_mac_binding(const struct flow *md,
>  }
>  
>  static void
> -run_put_mac_binding(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -                    const struct sbrec_mac_binding_table *mac_binding_table,
> -                    const struct put_mac_binding *pmb)
> +run_put_mac_binding(
> +    struct pinctrl_sbrec_txn *pinctrl_sbrec_txn,
> +    struct pinctrl_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +    struct pinctrl_sbrec_port_binding_by_tunnel_key_datapath *sbrec_port_binding_by_key,
> +    const struct pinctrl_sbrec_mac_binding_table *mac_binding_table,
> +    const struct put_mac_binding *pmb)
>  {
>      if (time_msec() > pmb->timestamp + 1000) {
>          return;
>      }
>  
>      /* Convert logical datapath and logical port key into lport. */
> -    const struct sbrec_port_binding *pb = lport_lookup_by_key(
> -        sbrec_datapath_binding_by_key, sbrec_port_binding_by_key,
> -        pmb->dp_key, pmb->port_key);
> +    const struct pinctrl_sbrec_datapath_binding *db =
> +        pinctrl_sbrec_datapath_binding_by_tunnel_key_find(
> +            sbrec_datapath_binding_by_key, pmb->dp_key);
> +    const struct pinctrl_sbrec_port_binding *pb
> +        = (!db ? NULL
> +           : pinctrl_sbrec_port_binding_by_tunnel_key_datapath_find(
> +               sbrec_port_binding_by_key, pmb->port_key, db));
>      if (!pb) {
>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>  
> @@ -1734,47 +1743,48 @@ run_put_mac_binding(struct ovsdb_idl_txn *ovnsb_idl_txn,
>       * port.
>       *
>       * XXX This is not very efficient. */
> -    const struct sbrec_mac_binding *b;
> -    SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
> +    const struct pinctrl_sbrec_mac_binding *b;
> +    PINCTRL_SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
>          if (!strcmp(b->logical_port, pb->logical_port)
>              && !strcmp(b->ip, pmb->ip_s)) {
>              if (strcmp(b->mac, mac_string)) {
> -                sbrec_mac_binding_set_mac(b, mac_string);
> +                pinctrl_sbrec_mac_binding_set_mac(b, mac_string);
>              }
>              return;
>          }
>      }
>  
>      /* Add new IP-MAC binding for this logical port. */
> -    b = sbrec_mac_binding_insert(ovnsb_idl_txn);
> -    sbrec_mac_binding_set_logical_port(b, pb->logical_port);
> -    sbrec_mac_binding_set_ip(b, pmb->ip_s);
> -    sbrec_mac_binding_set_mac(b, mac_string);
> -    sbrec_mac_binding_set_datapath(b, pb->datapath);
> +    b = pinctrl_sbrec_mac_binding_insert(pinctrl_sbrec_txn);
> +    pinctrl_sbrec_mac_binding_set_logical_port(b, pb->logical_port);
> +    pinctrl_sbrec_mac_binding_set_ip(b, pmb->ip_s);
> +    pinctrl_sbrec_mac_binding_set_mac(b, mac_string);
> +    pinctrl_sbrec_mac_binding_set_datapath(b, pb->datapath);
>  }
>  
>  static void
> -run_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                     struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                     struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -                     const struct sbrec_mac_binding_table *mac_binding_table)
> +run_put_mac_bindings(
> +    struct pinctrl_sbrec_txn *pinctrl_sbrec_txn,
> +    struct pinctrl_sbrec_datapath_binding_by_tunnel_key *sbrec_datapath_binding_by_key,
> +    struct pinctrl_sbrec_port_binding_by_tunnel_key_datapath *sbrec_port_binding_by_key,
> +    const struct pinctrl_sbrec_mac_binding_table *mac_binding_table)
>  {
> -    if (!ovnsb_idl_txn) {
> +    if (!pinctrl_sbrec_txn) {
>          return;
>      }
>  
>      const struct put_mac_binding *pmb;
>      HMAP_FOR_EACH (pmb, hmap_node, &put_mac_bindings) {
> -        run_put_mac_binding(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
> +        run_put_mac_binding(pinctrl_sbrec_txn, sbrec_datapath_binding_by_key,
>                              sbrec_port_binding_by_key, mac_binding_table, pmb);
>      }
>      flush_put_mac_bindings();
>  }
>  
>  static void
> -wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn)
> +wait_put_mac_bindings(struct pinctrl_sbrec_txn *pinctrl_sbrec_txn)
>  {
> -    if (ovnsb_idl_txn && !hmap_is_empty(&put_mac_bindings)) {
> +    if (pinctrl_sbrec_txn && !hmap_is_empty(&put_mac_bindings)) {
>          poll_immediate_wake();
>      }
>  }
> @@ -1840,7 +1850,7 @@ add_garp(const char *name, ofp_port_t ofport, int tag,
>  
>  /* Add or update a vif for which GARPs need to be announced. */
>  static void
> -send_garp_update(const struct sbrec_port_binding *binding_rec,
> +send_garp_update(const struct pinctrl_sbrec_port_binding *binding_rec,
>                   struct simap *localnet_ofports,
>                   const struct hmap *local_datapaths,
>                   struct shash *nat_addresses)
> @@ -1970,17 +1980,17 @@ send_garp(struct garp_data *garp, long long int current_time)
>  /* Get localnet vifs, local l3gw ports and ofport for localnet patch ports. */
>  static void
>  get_localnet_vifs_l3gwports(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct ovsrec_bridge *br_int,
> -    const struct sbrec_chassis *chassis,
> +    struct pinctrl_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct pinctrl_ovsrec_bridge *br_int,
> +    const struct pinctrl_sbrec_chassis *chassis,
>      const struct hmap *local_datapaths,
>      struct sset *localnet_vifs,
>      struct simap *localnet_ofports,
>      struct sset *local_l3gw_ports)
>  {
>      for (int i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> +        const struct pinctrl_ovsrec_port *port_rec = br_int->ports[i];
>          if (!strcmp(port_rec->name, br_int->name)) {
>              continue;
>          }
> @@ -1992,7 +2002,8 @@ get_localnet_vifs_l3gwports(
>          const char *localnet = smap_get(&port_rec->external_ids,
>                                          "ovn-localnet-port");
>          for (int j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
> +            const struct pinctrl_ovsrec_interface *iface_rec
> +                = port_rec->interfaces[j];
>              if (!iface_rec->n_ofport) {
>                  continue;
>              }
> @@ -2011,8 +2022,9 @@ get_localnet_vifs_l3gwports(
>              if (!iface_id) {
>                  continue;
>              }
> -            const struct sbrec_port_binding *pb
> -                = lport_lookup_by_name(sbrec_port_binding_by_name, iface_id);
> +            const struct pinctrl_sbrec_port_binding *pb
> +                = pinctrl_sbrec_port_binding_by_logical_port_find(
> +                    sbrec_port_binding_by_name, iface_id);
>              if (!pb) {
>                  continue;
>              }
> @@ -2025,12 +2037,13 @@ get_localnet_vifs_l3gwports(
>          }
>      }
>  
> -    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_datapath);
> +    struct pinctrl_sbrec_port_binding *target
> +        = pinctrl_sbrec_port_binding_by_datapath_new_row(
> +            sbrec_port_binding_by_datapath);
>  
>      const struct local_datapath *ld;
>      HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
> -        const struct sbrec_port_binding *pb;
> +        const struct pinctrl_sbrec_port_binding *pb;
>  
>          if (!ld->localnet_port) {
>              continue;
> @@ -2041,27 +2054,30 @@ get_localnet_vifs_l3gwports(
>           * bindings of type "patch" since they might connect to
>           * distributed gateway ports with NAT addresses. */
>  
> -        sbrec_port_binding_index_set_datapath(target, ld->datapath);
> -        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
> -                                           sbrec_port_binding_by_datapath) {
> +        pinctrl_sbrec_port_binding_by_datapath_set(
> +            target, pinctrl_sbrec_datapath_binding_get(ld->datapath));
> +        PINCTRL_SBREC_PORT_BINDING_BY_DATAPATH_FOR_EACH_EQUAL (
> +            pb, target, sbrec_port_binding_by_datapath) {
>              if ((ld->has_local_l3gateway && !strcmp(pb->type, "l3gateway"))
>                  || !strcmp(pb->type, "patch")) {
>                  sset_add(local_l3gw_ports, pb->logical_port);
>              }
>          }
>      }
> -    sbrec_port_binding_index_destroy_row(target);
> +    pinctrl_sbrec_port_binding_by_datapath_destroy_row(target);
>  }
>  
>  static bool
> -pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                            struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                            const struct sbrec_chassis *chassis,
> -                            const struct sset *active_tunnels,
> -                            const char *port_name)
> +pinctrl_is_chassis_resident(
> +    struct pinctrl_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct pinctrl_sbrec_chassis *chassis,
> +    const struct sset *active_tunnels,
> +    const char *port_name)
>  {
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(sbrec_port_binding_by_name, port_name);
> +    const struct pinctrl_sbrec_port_binding *pb
> +        = pinctrl_sbrec_port_binding_by_logical_port_find(
> +            sbrec_port_binding_by_name, port_name);
>      if (!pb || !pb->chassis) {
>          return false;
>      }
> @@ -2069,10 +2085,12 @@ pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_chassis_by_name,
>          return pb->chassis == chassis;
>      } else {
>          struct ovs_list *gateway_chassis =
> -            gateway_chassis_get_ordered(sbrec_chassis_by_name, pb);
> -        bool active = gateway_chassis_is_active(gateway_chassis,
> -                                                chassis,
> -                                                active_tunnels);
> +            gateway_chassis_get_ordered(
> +                (struct sbrec_chassis_index *) sbrec_chassis_by_name /* XXX */,
> +                (struct sbrec_port_binding *) pb /* XXX */);
> +        bool active = gateway_chassis_is_active(
> +            gateway_chassis, (struct sbrec_chassis *) chassis /* XXX */,
> +            active_tunnels);
>          gateway_chassis_destroy(gateway_chassis);
>          return active;
>      }
> @@ -2145,14 +2163,15 @@ extract_addresses_with_port(const char *addresses,
>  }
>  
>  static void
> -consider_nat_address(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                     const char *nat_address,
> -                     const struct sbrec_port_binding *pb,
> -                     struct sset *nat_address_keys,
> -                     const struct sbrec_chassis *chassis,
> -                     const struct sset *active_tunnels,
> -                     struct shash *nat_addresses)
> +consider_nat_address(
> +    struct pinctrl_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const char *nat_address,
> +    const struct pinctrl_sbrec_port_binding *pb,
> +    struct sset *nat_address_keys,
> +    const struct pinctrl_sbrec_chassis *chassis,
> +    const struct sset *active_tunnels,
> +    struct shash *nat_addresses)
>  {
>      struct lport_addresses *laddrs = xmalloc(sizeof *laddrs);
>      char *lport = NULL;
> @@ -2179,19 +2198,20 @@ consider_nat_address(struct ovsdb_idl_index *sbrec_chassis_by_name,
>  }
>  
>  static void
> -get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                           struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                           struct sset *nat_address_keys,
> -                           struct sset *local_l3gw_ports,
> -                           const struct sbrec_chassis *chassis,
> -                           const struct sset *active_tunnels,
> -                           struct shash *nat_addresses)
> +get_nat_addresses_and_keys(
> +    struct pinctrl_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    struct sset *nat_address_keys,
> +    struct sset *local_l3gw_ports,
> +    const struct pinctrl_sbrec_chassis *chassis,
> +    const struct sset *active_tunnels,
> +    struct shash *nat_addresses)
>  {
>      const char *gw_port;
>      SSET_FOR_EACH(gw_port, local_l3gw_ports) {
> -        const struct sbrec_port_binding *pb;
> -
> -        pb = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
> +        const struct pinctrl_sbrec_port_binding *pb =
> +            pinctrl_sbrec_port_binding_by_logical_port_find(
> +                sbrec_port_binding_by_name, gw_port);
>          if (!pb) {
>              continue;
>          }
> @@ -2229,13 +2249,14 @@ send_garp_wait(void)
>  }
>  
>  static void
> -send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
> -              struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -              struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -              const struct ovsrec_bridge *br_int,
> -              const struct sbrec_chassis *chassis,
> -              const struct hmap *local_datapaths,
> -              const struct sset *active_tunnels)
> +send_garp_run(
> +    struct pinctrl_sbrec_chassis_by_name *sbrec_chassis_by_name,
> +    struct pinctrl_sbrec_port_binding_by_datapath *sbrec_port_binding_by_datapath,
> +    struct pinctrl_sbrec_port_binding_by_logical_port *sbrec_port_binding_by_name,
> +    const struct pinctrl_ovsrec_bridge *br_int,
> +    const struct pinctrl_sbrec_chassis *chassis,
> +    const struct hmap *local_datapaths,
> +    const struct sset *active_tunnels)
>  {
>      struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs);
>      struct sset local_l3gw_ports = SSET_INITIALIZER(&local_l3gw_ports);
> @@ -2268,8 +2289,9 @@ send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>      /* Update send_garp_data. */
>      const char *iface_id;
>      SSET_FOR_EACH (iface_id, &localnet_vifs) {
> -        const struct sbrec_port_binding *pb = lport_lookup_by_name(
> -            sbrec_port_binding_by_name, iface_id);
> +        const struct pinctrl_sbrec_port_binding *pb
> +            = pinctrl_sbrec_port_binding_by_logical_port_find(
> +                sbrec_port_binding_by_name, iface_id);
>          if (pb) {
>              send_garp_update(pb, &localnet_ofports, local_datapaths,
>                               &nat_addresses);
> @@ -2279,8 +2301,9 @@ send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
>      /* Update send_garp_data for nat-addresses. */
>      const char *gw_port;
>      SSET_FOR_EACH (gw_port, &local_l3gw_ports) {
> -        const struct sbrec_port_binding *pb
> -            = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
> +        const struct pinctrl_sbrec_port_binding *pb
> +            = pinctrl_sbrec_port_binding_by_logical_port_find(
> +                sbrec_port_binding_by_name, gw_port);
>          if (pb) {
>              send_garp_update(pb, &localnet_ofports, local_datapaths,
>                               &nat_addresses);
> diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
> index 60014ebe37b2..5e75fc678262 100644
> --- a/ovn/controller/pinctrl.h
> +++ b/ovn/controller/pinctrl.h
> @@ -17,33 +17,30 @@
>  #ifndef PINCTRL_H
>  #define PINCTRL_H 1
>  
> +#include "ovn/controller/pinctrl-vswitch-idl.h"
> +#include "ovn/controller/pinctrl-ovn-sb-idl.h"
> +
>  #include <stdint.h>
>  
>  #include "lib/sset.h"
>  #include "openvswitch/meta-flow.h"
>  
>  struct hmap;
> -struct lport_index;
> -struct ovsdb_idl_index;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct sbrec_chassis;
> -struct sbrec_dns_table;
> -struct sbrec_mac_binding_table;
>  
>  void pinctrl_init(void);
> -void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                 struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                 struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                 const struct sbrec_dns_table *,
> -                 const struct sbrec_mac_binding_table *,
> -                 const struct ovsrec_bridge *, const struct sbrec_chassis *,
> +void pinctrl_run(struct pinctrl_sbrec_txn *,
> +                 struct pinctrl_sbrec_chassis_by_name *,
> +                 struct pinctrl_sbrec_datapath_binding_by_tunnel_key *,
> +                 struct pinctrl_sbrec_port_binding_by_datapath *,
> +                 struct pinctrl_sbrec_port_binding_by_tunnel_key_datapath *,
> +                 struct pinctrl_sbrec_port_binding_by_logical_port *,
> +                 const struct pinctrl_sbrec_dns_table *,
> +                 const struct pinctrl_sbrec_mac_binding_table *,
> +                 const struct pinctrl_ovsrec_bridge *,
> +                 const struct pinctrl_sbrec_chassis *,
>                   const struct hmap *local_datapaths,
>                   const struct sset *active_tunnels);
> -void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
> +void pinctrl_wait(struct pinctrl_sbrec_txn *);
>  void pinctrl_destroy(void);
>  
>  #endif /* ovn/pinctrl.h */
> diff --git a/ovn/lib/chassis-index.c b/ovn/lib/chassis-index.c
> index a5dbf4ace5da..0d62bbe87397 100644
> --- a/ovn/lib/chassis-index.c
> +++ b/ovn/lib/chassis-index.c
> @@ -16,16 +16,16 @@
>  #include "ovn/lib/chassis-index.h"
>  #include "ovn/lib/ovn-sb-idl.h"
>  
> -struct ovsdb_idl_index *
> +struct sbrec_chassis_index *
>  chassis_index_create(struct ovsdb_idl *idl)
>  {
> -    return ovsdb_idl_index_create1(idl, &sbrec_chassis_col_name);
> +    return sbrec_chassis_index_create1(idl, &sbrec_chassis_col_name);
>  }
>  
>  /* Finds and returns the chassis with the given 'name', or NULL if no such
>   * chassis exists. */
>  const struct sbrec_chassis *
> -chassis_lookup_by_name(struct ovsdb_idl_index *sbrec_chassis_by_name,
> +chassis_lookup_by_name(struct sbrec_chassis_index *sbrec_chassis_by_name,
>                         const char *name)
>  {
>      struct sbrec_chassis *target = sbrec_chassis_index_init_row(
> diff --git a/ovn/lib/chassis-index.h b/ovn/lib/chassis-index.h
> index d5e5df926723..e873882120ce 100644
> --- a/ovn/lib/chassis-index.h
> +++ b/ovn/lib/chassis-index.h
> @@ -18,9 +18,9 @@
>  
>  struct ovsdb_idl;
>  
> -struct ovsdb_idl_index *chassis_index_create(struct ovsdb_idl *);
> +struct sbrec_chassis_index *chassis_index_create(struct ovsdb_idl *);
>  
>  const struct sbrec_chassis *chassis_lookup_by_name(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name);
> +    struct sbrec_chassis_index *sbrec_chassis_by_name, const char *name);
>  
>  #endif /* ovn/lib/chassis-index.h */
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 72fe4e795871..5521f9923f40 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -1725,7 +1725,7 @@ gateway_chassis_equal(const struct nbrec_gateway_chassis *nb_gwc,
>  
>  static bool
>  sbpb_gw_chassis_needs_update(
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> +    struct sbrec_chassis_index *sbrec_chassis_by_name,
>      const struct sbrec_port_binding *port_binding,
>      const struct nbrec_logical_router_port *lrp)
>  {
> @@ -1809,7 +1809,7 @@ sbpb_gw_chassis_needs_update(
>  static void
>  copy_gw_chassis_from_nbrp_to_sbpb(
>          struct northd_context *ctx,
> -        struct ovsdb_idl_index *sbrec_chassis_by_name,
> +        struct sbrec_chassis_index *sbrec_chassis_by_name,
>          const struct nbrec_logical_router_port *lrp,
>          const struct sbrec_port_binding *port_binding) {
>  
> @@ -1856,7 +1856,7 @@ copy_gw_chassis_from_nbrp_to_sbpb(
>  
>  static void
>  ovn_port_update_sbrec(struct northd_context *ctx,
> -                      struct ovsdb_idl_index *sbrec_chassis_by_name,
> +                      struct sbrec_chassis_index *sbrec_chassis_by_name,
>                        const struct ovn_port *op,
>                        struct hmap *chassis_qdisc_queues)
>  {
> @@ -2103,7 +2103,7 @@ cleanup_mac_bindings(struct northd_context *ctx, struct hmap *ports)
>   * datapaths. */
>  static void
>  build_ports(struct northd_context *ctx,
> -            struct ovsdb_idl_index *sbrec_chassis_by_name,
> +            struct sbrec_chassis_index *sbrec_chassis_by_name,
>              struct hmap *datapaths, struct hmap *ports)
>  {
>      struct ovs_list sb_only, nb_only, both;
> @@ -6625,7 +6625,7 @@ sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
>  
>  static void
>  ovnnb_db_run(struct northd_context *ctx,
> -             struct ovsdb_idl_index *sbrec_chassis_by_name,
> +             struct sbrec_chassis_index *sbrec_chassis_by_name,
>               struct ovsdb_idl_loop *sb_loop)
>  {
>      if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
> @@ -7269,7 +7269,7 @@ main(int argc, char *argv[])
>      ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
>      ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
>  
> -    struct ovsdb_idl_index *sbrec_chassis_by_name
> +    struct sbrec_chassis_index *sbrec_chassis_by_name
>          = chassis_index_create(ovnsb_idl_loop.idl);
>  
>      /* Ensure that only a single ovn-northd is active in the deployment by
> diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
> index ee655f7e7915..a01c97775ae4 100755
> --- a/ovsdb/ovsdb-idlc.in
> +++ b/ovsdb/ovsdb-idlc.in
> @@ -14,7 +14,9 @@ from ovs.db.types import StringType, IntegerType, RealType
>  argv0 = sys.argv[0]
>  
>  def parseSchema(filename):
> -    return ovs.db.schema.IdlSchema.from_json(ovs.json.from_file(filename))
> +    schema = ovs.db.schema.IdlSchema.from_json(ovs.json.from_file(filename))
> +    replace_cplusplus_keyword(schema)
> +    return schema
>  
>  def annotateSchema(schemaFile, annotationFile):
>      schemaJson = ovs.json.from_file(schemaFile)
> @@ -82,6 +84,8 @@ def cMembers(prefix, tableName, columnName, column, const, refTable=True):
>          m = {'name': columnName,
>               'type': constify(type.key.toCType(prefix, refTable) + pointer, const),
>               'comment': type.cDeclComment()}
> +        if refTable and type.key.ref_table_name:
> +            m['cast'] = '(%s)' % m['type']
>  
>          if singleton:
>              comment += " * '%s'" % columnName
> @@ -182,7 +186,6 @@ def replace_cplusplus_keyword(schema):
>  
>  def printCIDLHeader(schemaFile):
>      schema = parseSchema(schemaFile)
> -    replace_cplusplus_keyword(schema)
>      prefix = schema.idlPrefix
>      print('''\
>  /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
> @@ -283,19 +286,27 @@ static inline bool %(s)s_is_deleted(const struct %(s)s *row)
>      return %(s)s_row_get_seqno(row, OVSDB_IDL_CHANGE_DELETE) > 0;
>  }
>  
> +struct %(s)s_index *%(s)s_index_create(
> +    struct ovsdb_idl *, const struct ovsdb_idl_index_column[], size_t n);
> +struct %(s)s_index *%(s)s_index_create1(
> +    struct ovsdb_idl *, const struct ovsdb_idl_column *);
> +struct %(s)s_index *%(s)s_index_create2(
> +    struct ovsdb_idl *, const struct ovsdb_idl_column *,
> +    const struct ovsdb_idl_column *);
> +
>  void %(s)s_index_destroy_row(const struct %(s)s *);
>  
> -struct %(s)s *%(s)s_index_find(struct ovsdb_idl_index *, const struct %(s)s *);
> +struct %(s)s *%(s)s_index_find(struct %(s)s_index *, const struct %(s)s *);
>  
>  int %(s)s_index_compare(
> -    struct ovsdb_idl_index *, 
> +    struct %(s)s_index *, 
>      const struct %(s)s *, 
>      const struct %(s)s *);
> -struct ovsdb_idl_cursor %(s)s_cursor_first(struct ovsdb_idl_index *);
> +struct ovsdb_idl_cursor %(s)s_cursor_first(struct %(s)s_index *);
>  struct ovsdb_idl_cursor %(s)s_cursor_first_eq(
> -    struct ovsdb_idl_index *, const struct %(s)s *);
> +    struct %(s)s_index *, const struct %(s)s *);
>  struct ovsdb_idl_cursor %(s)s_cursor_first_ge(
> -    struct ovsdb_idl_index *, const struct %(s)s *);
> +    struct %(s)s_index *, const struct %(s)s *);
>  
>  struct %(s)s *%(s)s_cursor_data(struct ovsdb_idl_cursor *);
>  
> @@ -386,7 +397,7 @@ bool %(s)s_is_updated(const struct %(s)s *, enum %(s)s_column_id);
>          print("")
>  
>      # Table indexes.
> -        print("struct %(s)s *%(s)s_index_init_row(struct ovsdb_idl_index *);" % {'s': structName})
> +        print("struct %(s)s *%(s)s_index_init_row(struct %(s)s_index *);" % {'s': structName})
>          print
>          for columnName, column in sorted(table.columns.items()):
>              print('void %(s)s_index_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName})
> @@ -431,7 +442,6 @@ def printEnum(type, members):
>  
>  def printCIDLSource(schemaFile):
>      schema = parseSchema(schemaFile)
> -    replace_cplusplus_keyword(schema)
>      prefix = schema.idlPrefix
>      print('''\
>  /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
> @@ -1177,6 +1187,51 @@ void
>  
>  # Index table related functions
>          print("""
> +static bool
> +%(s)s_column_in_table(
> +    struct ovsdb_idl *idl,
> +    const struct ovsdb_idl_index_column *column)
> +{
> +    const struct ovsdb_idl_class *class = ovsdb_idl_get_class(idl);
> +    const struct ovsdb_idl_table_class *tc
> +        = ovsdb_idl_table_class_from_column(class, column->column);
> +    return tc == &%(p)stable_%(tl)s;
> +}
> +
> +struct %(s)s_index *
> +%(s)s_index_create(
> +    struct ovsdb_idl *idl,
> +    const struct ovsdb_idl_index_column columns[], size_t n)
> +{
> +    /* It's only necessary to check the first column's table because
> +     * ovsdb_idl_index_create() checks that all of the columns are
> +     * in the same table. */
> +    ovs_assert(n > 0);
> +    ovs_assert(%(s)s_column_in_table(idl, &columns[0]));
> +
> +    return (struct %(s)s_index *) ovsdb_idl_index_create(idl, columns, n);
> +}
> +
> +struct %(s)s_index *%(s)s_index_create1(
> +    struct ovsdb_idl *idl, const struct ovsdb_idl_column *column1)
> +{
> +    const struct ovsdb_idl_index_column columns[] = {
> +        { .column = column1 },
> +    };
> +    return %(s)s_index_create(idl, columns, ARRAY_SIZE(columns));
> +}
> +
> +struct %(s)s_index *%(s)s_index_create2(
> +    struct ovsdb_idl *idl, const struct ovsdb_idl_column *column1,
> +    const struct ovsdb_idl_column *column2)
> +{
> +    const struct ovsdb_idl_index_column columns[] = {
> +        { .column = column1 },
> +        { .column = column2 },
> +    };
> +    return %(s)s_index_create(idl, columns, ARRAY_SIZE(columns));
> +}
> +
>  /* Destroy 'row' of kind "%(t)s". The row must have been
>   * created with ovsdb_idl_index_init_row.
>   */
> @@ -1184,55 +1239,63 @@ void
>  %(s)s_index_destroy_row(const struct %(s)s *row)
>  {
>      ovsdb_idl_index_destroy_row(&row->header_);
> -}
> -        """ % { 's' : structName, 't': tableName })
> +}"""
> +              % { 's' : structName, 't': tableName, 'p': prefix,
> +                  'tl': tableName.lower()})
>          print("""
> +static inline struct ovsdb_idl_index *
> +%(s)s_index_cast(struct %(s)s_index *index_)
> +{
> +    struct ovsdb_idl_index *index = (struct ovsdb_idl_index *) index_;
> +    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
> +    return index;
> +}
> +
>  /* Creates a new row of kind "%(t)s". */
>  struct %(s)s *
> -%(s)s_index_init_row(struct ovsdb_idl_index *index)
> +%(s)s_index_init_row(struct %(s)s_index *index)
>  {
> -    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
> -    return (struct %(s)s *) ovsdb_idl_index_init_row(index);
> +    return (struct %(s)s *) ovsdb_idl_index_init_row(%(s)s_index_cast(index));
>  }
>  
>  struct %(s)s *
> -%(s)s_index_find(struct ovsdb_idl_index *index, const struct %(s)s *target)
> +%(s)s_index_find(struct %(s)s_index *index, const struct %(s)s *target)
>  {
> -    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
> -    return %(s)s_cast(ovsdb_idl_index_find(index, &target->header_));
> +    return %(s)s_cast(
> +        ovsdb_idl_index_find(%(s)s_index_cast(index), &target->header_));
>  }
>  
>  /* Compares 'a' to 'b' and returns a strcmp()-type result. */
>  int
>  %(s)s_index_compare(
> -    struct ovsdb_idl_index *index, 
> +    struct %(s)s_index *index, 
>      const struct %(s)s *a, 
>      const struct %(s)s *b)
>  {
> -    return ovsdb_idl_index_compare(index, &a->header_, &b->header_);
> +    return ovsdb_idl_index_compare(%(s)s_index_cast(index),
> +                                   &a->header_, &b->header_);
>  }
>  
>  struct ovsdb_idl_cursor
> -%(s)s_cursor_first(struct ovsdb_idl_index *index)
> +%(s)s_cursor_first(struct %(s)s_index *index)
>  {
> -    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
> -    return ovsdb_idl_cursor_first(index);
> +    return ovsdb_idl_cursor_first(%(s)s_index_cast(index));
>  }
>  
>  struct ovsdb_idl_cursor
>  %(s)s_cursor_first_eq(
> -    struct ovsdb_idl_index *index, const struct %(s)s *target)
> +    struct %(s)s_index *index, const struct %(s)s *target)
>  {
> -    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
> -    return ovsdb_idl_cursor_first_eq(index, &target->header_);
> +    return ovsdb_idl_cursor_first_eq(%(s)s_index_cast(index),
> +                                     &target->header_);
>  }
>  
>  struct ovsdb_idl_cursor
>  %(s)s_cursor_first_ge(
> -    struct ovsdb_idl_index *index, const struct %(s)s *target)
> +    struct %(s)s_index *index, const struct %(s)s *target)
>  {
> -    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
> -    return ovsdb_idl_cursor_first_ge(index, &target->header_);
> +    return ovsdb_idl_cursor_first_ge(%(s)s_index_cast(index),
> +                                     &target->header_);
>  }
>  
>  struct %(s)s *
> @@ -1493,6 +1556,451 @@ def ovsdb_escape(string):
>              return '\\x%02x' % ord(c)
>      return re.sub(r'["\\\000-\037]', escape, string)
>  
> +RO = 1
> +WO = 2
> +RW = RO|WO
> +def printCIDLSubset(schemaFile, subsetFile):
> +    schema = parseSchema(schemaFile)
> +    prefix = schema.idlPrefix
> +    subset = eval(open(subsetFile, "rb").read(), None,
> +                  {'RO': RO,
> +                   'RW': RW,
> +                   'WO': WO})
> +    subset_prefix = subset['prefix']
> +    subset_tables = subset['tables']
> +    subset_indexes = subset.get('indexes', {})
> +
> +    # XXX verify that columns in the subset_tables exist
> +    # XXX verify that permitted columns only reference permitted tables
> +
> +    # Columns named in an index must be RW
> +    for table_name, indexes in subset_indexes.items():
> +        column_acls = subset_tables.setdefault(table_name, {})
> +        for index in indexes:
> +            for column_name in index:
> +                column_acls[column_name] = RW
> +
> +    print('''\
> +/* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
> +
> +#ifndef %(PREFIX)sIDL_HEADER
> +#define %(PREFIX)sIDL_HEADER 1
> +
> +#include "util.h"
> +#include %(header)s
> +
> +#ifdef  __cplusplus
> +extern "C" {
> +#endif
> +
> +/* Allows the IDL owner to obtain a restricted transaction view. */
> +static inline struct %(prefix)stxn *
> +%(prefix)stxn_get(struct ovsdb_idl_txn *txn)
> +{
> +    return (struct %(prefix)stxn *) txn;
> +}
> +
> +static inline void OVS_PRINTF_FORMAT(2, 3)
> +%(prefix)stxn_add_comment(struct %(prefix)stxn *txn_, const char *format, ...)
> +{
> +    struct ovsdb_idl_txn *txn = (struct ovsdb_idl_txn *) txn_;
> +
> +    va_list args;
> +    va_start(args, format);
> +    char *comment = xvasprintf(format, args);
> +    va_end(args);
> +
> +    ovsdb_idl_txn_add_comment(txn, "%%s", comment);
> +
> +    free(comment);
> +}
> +
> +static inline void
> +%(prefix)sregister(struct ovsdb_idl *idl)
> +{\
> +'''
> +          % {'prefix': subset_prefix,
> +             'PREFIX': subset_prefix.upper(),
> +             'header': schema.idlHeader})
> +
> +    for table_name, column_acls in sorted(subset_tables.items()):
> +        table = schema.tables[table_name]
> +        for column_name, column in sorted_columns(table):
> +            column_permissions = column_acls.get(column_name, 0)
> +            if not column_permissions:
> +                continue
> +            print('    ovsdb_idl_add_column(idl, &%(p)s%(t)s_col_%(c)s);'
> +                  % {'p': prefix,
> +                     't': table_name.lower(),
> +                     'c': column_name})
> +    print('}')
> +
> +    for table_name, column_acls in sorted(subset_tables.items()):
> +        table = schema.tables[table_name]
> +        struct_name = "%s%s" % (subset_prefix, table_name.lower())
> +        prefix_struct_name = "%s%s" % (prefix, table_name.lower())
> +
> +        print("")
> +        print("/* %s table. */" % table_name)
> +        print("struct %s {" % struct_name)
> +        print("\tstruct ovsdb_idl_row header_;")
> +        table_permissions = 0
> +        for column_name, column in sorted_columns(table):
> +            print("\n\t/* %s column. */" % column_name)
> +            if column.extensions.get("members"):
> +                print("\t%s" % column.extensions["members"])
> +                continue
> +            comment, members = cMembers(subset_prefix, table_name,
> +                                        column_name, column, False)
> +            column_permissions = column_acls.get(column_name, 0)
> +            table_permissions |= column_permissions
> +            name_suffix = ('' if column_permissions & RO
> +                           else '__writeonly' if column_permissions & WO
> +                           else '__noaccess')
> +            for member in members:
> +                print("\t%s%s%s;%s"
> +                      % (member['type'],
> +                         member['name'],
> +                         name_suffix,
> +                         member['comment']))
> +        print("};")
> +
> +        args = {'s': struct_name, 'S': struct_name.upper(),
> +                'sp': prefix_struct_name, 'p': subset_prefix}
> +        print('''
> +/* Allows the IDL owner to obtain a restricted table view. */
> +static inline const struct %(s)s_table *
> +%(s)s_table_get(const struct ovsdb_idl *idl)
> +{
> +    return (const struct %(s)s_table *) idl;
> +}
> +
> +/* Obtains a restricted row view from a general-purpose one. */
> +static inline const struct %(s)s *
> +%(s)s_get(const struct %(sp)s *row)
> +{
> +    return (const struct %(s)s *) row;
> +}
> +
> +/* Iterators for the restricted table view. */
> +#define %(S)s_TABLE_FOR_EACH(ROW, TABLE) \\
> +        for ((ROW) = %(s)s_table_first(TABLE); \\
> +             (ROW); \\
> +             (ROW) = %(s)s_next(ROW))''' % args)
> +        if table_permissions & WO:
> +            print('''\
> +#define %(S)s_TABLE_FOR_EACH_SAFE(ROW, NEXT, TABLE) \\
> +        for ((ROW) = %(s)s_table_first(TABLE); \\
> +             (ROW) ? ((NEXT) = %(s)s_next(ROW), 1) : 0; \\
> +             (ROW) = (NEXT))''' % args)
> +        print('''\
> +static inline const struct %(s)s *
> +%(s)s_get_for_uuid(
> +    const struct %(s)s_table *table,
> +    const struct uuid *uuid)
> +{
> +    struct ovsdb_idl *idl = (struct ovsdb_idl *) table;
> +    return (const struct %(s)s *) %(sp)s_get_for_uuid(idl, uuid);
> +}
> +static inline const struct %(s)s *
> +%(s)s_table_first(const struct %(s)s_table *table)
> +{
> +    struct ovsdb_idl *idl = (struct ovsdb_idl *) table;
> +    return (const struct %(s)s *) %(sp)s_first(idl);
> +}
> +static inline const struct %(s)s *
> +%(s)s_next(const struct %(s)s *row)
> +{
> +    return (const struct %(s)s *) %(sp)s_next(
> +        (struct %(sp)s *) row);
> +}''' % {'s': struct_name,
> +        'sp': prefix_struct_name,
> +        'c': column_name})
> +        if table_permissions & WO:
> +            print('''
> +static inline void
> +%(s)s_delete(const struct %(s)s *row_)
> +{
> +    struct %(sp)s *row = (struct %(sp)s *) row_;
> +    %(sp)s_delete(row);
> +}
> +
> +static inline struct %(s)s *
> +%(s)s_insert(struct %(p)stxn *txn_)
> +{
> +    struct ovsdb_idl_txn *txn = (struct ovsdb_idl_txn *) txn_;
> +    return (struct %(s)s *) %(sp)s_insert(txn);
> +}
> +
> +
> +unsigned int %(s)s_row_get_seqno(const struct %(s)s *, enum ovsdb_idl_change);
> +const struct %(s)s *%(s)s_track_get_first(const struct %(s)s_table *);
> +const struct %(s)s *%(s)s_track_get_next(const struct %(s)s *);
> +#define %(S)s_FOR_EACH_TRACKED(ROW, IDL) \\
> +        for ((ROW) = %(s)s_track_get_first(IDL); \\
> +             (ROW); \\
> +             (ROW) = %(s)s_track_get_next(ROW))
> +
> +/* Returns true if 'row' was inserted since the last change tracking reset. */
> +static inline bool %(s)s_is_new(const struct %(s)s *row)
> +{
> +    return %(s)s_row_get_seqno(row, OVSDB_IDL_CHANGE_MODIFY) == 0;
> +}
> +
> +/* Returns true if 'row' was deleted since the last change tracking reset. */
> +static inline bool %(s)s_is_deleted(const struct %(s)s *row)
> +{
> +    return %(s)s_row_get_seqno(row, OVSDB_IDL_CHANGE_DELETE) > 0;
> +}
> +
> +bool %(s)s_is_updated(const struct %(s)s *, enum %(sp)s_column_id);''' % args)
> +
> +        for column_name, column in sorted_columns(table):
> +            if (column.extensions.get('synthetic')
> +                or (column_acls.get(column_name, 0) & RO) == 0):
> +                continue
> +            print('''
> +static inline void
> +%(s)s_verify_%(c)s(const struct %(s)s *row)
> +{
> +    %(sp)s_verify_%(c)s((const struct %(sp)s *) row);
> +}''' % {'s': struct_name,
> +        'sp': prefix_struct_name,
> +        'c': column_name})
> +
> +        print("")
> +        for column_name, column in sorted_columns(table):
> +            if (column.extensions.get('synthetic')
> +                or (column_acls.get(column_name, 0) & RO) == 0):
> +                continue
> +            if column.type.value:
> +                valueParam = ', enum ovsdb_atomic_type value_type'
> +            else:
> +                valueParam = ''
> +            print('const struct ovsdb_datum *%(s)s_get_%(c)s(const struct %(s)s *, enum ovsdb_atomic_type key_type%(v)s);' % {
> +                's': struct_name, 'c': column_name, 'v': valueParam})
> +
> +        print("")
> +        for column_name, column in sorted_columns(table):
> +            if (column.extensions.get('synthetic')
> +                or (column_acls.get(column_name, 0) & WO) == 0):
> +                continue
> +            type = column.type
> +
> +            if type.is_smap():
> +                print('''\
> +static inline void
> +%(s)s_set_%(c)s(const struct %(s)s *row, const struct smap *smap)
> +{
> +    %(sp)s_set_%(c)s((const struct %(sp)s *) row, smap);
> +}
> +'''
> +                      % {'c': column_name,
> +                         's': struct_name,
> +                         'sp': prefix_struct_name})
> +                continue
> +
> +            comment, members = cMembers(subset_prefix, table_name, column_name,
> +                                        column, True)
> +            comment2, members2 = cMembers(prefix, table_name, column_name,
> +                                          column, True)
> +
> +            print('''\
> +static inline void
> +%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)
> +{
> +    %(sp)s_set_%(c)s((const struct %(sp)s *) row, %(arg_names)s);
> +}
> +'''
> +                  % {'c': column_name,
> +                     's': struct_name,
> +                     'sp': prefix_struct_name,
> +                     'args': ', '.join(['%(type)s%(name)s' % m for m in members]),
> +                     'arg_names': ', '.join([m.get('cast', '') + m['name']
> +                                             for m in members2])})
> +
> +        print("")
> +        for column_name, column in sorted_columns(table):
> +            if (column.extensions.get('synthetic')
> +                or column_acls.get(column_name, '') != RW):
> +                continue
> +            if column.type.is_map():
> +                print('void %(s)s_update_%(c)s_setkey(const struct %(s)s *, ' % {'s': struct_name, 'c': column_name}, end=' ')
> +                print('%(coltype)s, %(valtype)s);' % {'coltype':column.type.key.to_const_c_type(subset_prefix), 'valtype':column.type.value.to_const_c_type(subset_prefix)})
> +                print('void %(s)s_update_%(c)s_delkey(const struct %(s)s *, ' % {'s': struct_name, 'c': column_name}, end=' ')
> +                print('%(coltype)s);' % {'coltype':column.type.key.to_const_c_type(subset_prefix)})
> +            if column.type.is_set():
> +                args = {
> +                    'sp': prefix_struct_name,
> +                    's': struct_name,
> +                    'c': column_name,
> +                    'valtype': column.type.key.to_const_c_type(subset_prefix)
> +                }
> +                args['cast'] = '(%s)' % column.type.key.to_const_c_type(prefix) if column.type.key.ref_table_name else ''
> +                print('''\
> +static inline void
> +%(s)s_update_%(c)s_addvalue(
> +    const struct %(s)s *row,
> +    %(valtype)s new_value)
> +{
> +    %(sp)s_update_%(c)s_addvalue(
> +        (const struct %(sp)s *) row,
> +        %(cast)snew_value);
> +}
> +
> +static inline void
> +%(s)s_update_%(c)s_delvalue(
> +    const struct %(s)s *row,
> +    %(valtype)s old_value)
> +{
> +    %(sp)s_update_%(c)s_delvalue(
> +        (const struct %(sp)s *) row,
> +        %(cast)sold_value);
> +}''' % args)
> +
> +    for table_name, indexes in subset_indexes.items():
> +        table = schema.tables[table_name]
> +        struct_name = "%s%s" % (subset_prefix, table_name.lower())
> +        prefix_struct_name = "%s%s" % (prefix, table_name.lower())
> +
> +        for index in indexes:
> +            print('/* Index of %s by %s. */'
> +                  % (table_name, ' and '.join(index)))
> +
> +            columns = '_'.join(index)
> +
> +            set_params = []
> +            set_names = []
> +            set_calls = []
> +            for column_name in index:
> +                column = table.columns[column_name]
> +                comment, members = cMembers(subset_prefix, table_name,
> +                                            column_name, column, True)
> +                set_params += ['%(type)s%(name)s' % m for m in members]
> +                set_names += [m['name'] for m in members]
> +                set_calls += ['%(s)s_set_%(c)s(row, %(names)s);'
> +                              % {'s': struct_name,
> +                                 'c': column_name,
> +                                 'names': ', '.join([m['name'] for m in members])}]
> +
> +            index_name = '%s_by_%s' % (struct_name, columns)
> +            args = {'c': columns,
> +                    't': table_name.lower(),
> +                    's': struct_name,
> +                    'sp': prefix_struct_name,
> +                    'i': index_name,
> +                    'I': index_name.upper(),
> +                    'set_params': ', '.join(set_params),
> +                    'set_names': ', '.join(set_names),
> +                    'set_calls': '\n'.join(set_calls)}
> +            print('''\
> +static inline struct %(i)s *
> +%(i)s_get(struct %(sp)s_index *index)
> +{
> +    return (struct %(i)s *) index;
> +}
> +
> +static inline struct %(s)s *
> +%(i)s_new_row(struct %(i)s *index)
> +{
> +    return (struct %(s)s *) ovsdb_idl_index_init_row((struct ovsdb_idl_index *) index);
> +}
> +
> +static inline void
> +%(i)s_set(
> +    struct %(s)s *row,
> +    %(set_params)s)
> +{
> +    %(set_calls)s
> +}
> +
> +static inline void
> +%(i)s_destroy_row(
> +    struct %(s)s *row)
> +{
> +    ovsdb_idl_index_destroy_row((struct ovsdb_idl_row *) row);
> +}
> +
> +#define %(I)s_FOR_EACH_RANGE(ROW, FROM, TO, INDEX) \\
> +        for (struct ovsdb_idl_cursor cursor__ = %(i)s_cursor_first_ge(INDEX, FROM); \\
> +             (cursor__.position \\
> +              && ((ROW) = %(i)s_cursor_data(&cursor__), \\
> +                  !(TO) || %(i)s_index_compare(INDEX, ROW, TO) <= 0)); \\
> +             ovsdb_idl_cursor_next(&cursor__))
> +#define %(I)s_FOR_EACH_EQUAL(ROW, KEY, INDEX) \\
> +        for (struct ovsdb_idl_cursor cursor__ = %(i)s_cursor_first_eq(INDEX, KEY); \\
> +             (cursor__.position \\
> +              ? ((ROW) = %(i)s_cursor_data(&cursor__), \\
> +                 ovsdb_idl_cursor_next_eq(&cursor__), \\
> +                 true) \\
> +              : false); \\
> +            )
> +#define %(I)s_FOR_EACH_BYINDEX(ROW, INDEX) \\
> +        for (struct ovsdb_idl_cursor cursor__ = %(i)s_cursor_first(INDEX); \\
> +             (cursor__.position \\
> +              ? ((ROW) = %(i)s_cursor_data(&cursor__), \\
> +                 ovsdb_idl_cursor_next(&cursor__), \\
> +                 true) \\
> +              : false); \\
> +            )
> +
> +static inline struct %(s)s *
> +%(i)s_find(struct %(i)s *index, %(set_params)s)
> +{
> +    struct %(s)s *target = %(i)s_new_row(index);
> +    %(i)s_set(target, %(set_names)s);
> +    struct %(s)s *row = (struct %(s)s *) ovsdb_idl_index_find((struct ovsdb_idl_index *) index, &target->header_);
> +    %(i)s_destroy_row(target);
> +    return row;
> +}
> +
> +/* Compares 'a' to 'b' and returns a strcmp()-type result. */
> +static inline int
> +%(i)s_compare(
> +    struct %(i)s *index, 
> +    const struct %(s)s *a, 
> +    const struct %(s)s *b)
> +{
> +    return ovsdb_idl_index_compare((struct ovsdb_idl_index *) index,
> +                                   &a->header_, &b->header_);
> +}
> +
> +static inline struct ovsdb_idl_cursor
> +%(i)s_cursor_first(struct %(i)s *index)
> +{
> +    return ovsdb_idl_cursor_first((struct ovsdb_idl_index *) index);
> +}
> +
> +static inline struct ovsdb_idl_cursor
> +%(i)s_cursor_first_eq(
> +    struct %(i)s *index, const struct %(s)s *target)
> +{
> +    return ovsdb_idl_cursor_first_eq((struct ovsdb_idl_index *) index,
> +                                     &target->header_);
> +}
> +
> +static inline struct ovsdb_idl_cursor
> +%(i)s_cursor_first_ge(
> +    struct %(i)s *index, const struct %(s)s *target)
> +{
> +    return ovsdb_idl_cursor_first_ge((struct ovsdb_idl_index *) index,
> +                                     &target->header_);
> +}
> +
> +static inline struct %(s)s *
> +%(i)s_cursor_data(struct ovsdb_idl_cursor *cursor)
> +{
> +    return (struct %(s)s *) ovsdb_idl_cursor_data(cursor);
> +}
> +'''
> +                  % args)
> +
> +    print('''
> +#ifdef  __cplusplus
> +}
> +#endif''')
> +    print("\n#endif /* %sIDL_HEADER */" % subset_prefix.upper())
> +
>  def usage():
>      print("""\
>  %(argv0)s: ovsdb schema compiler
> @@ -1539,7 +2047,8 @@ if __name__ == "__main__":
>  
>          commands = {"annotate": (annotateSchema, 2),
>                      "c-idl-header": (printCIDLHeader, 1),
> -                    "c-idl-source": (printCIDLSource, 1)}
> +                    "c-idl-source": (printCIDLSource, 1),
> +                    "c-idl-subset": (printCIDLSubset, 2)}
>  
>          if not args[0] in commands:
>              sys.stderr.write("%s: unknown command \"%s\" "
> diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
> index 48706b71c719..231cc63e015e 100644
> --- a/tests/test-ovsdb.c
> +++ b/tests/test-ovsdb.c
> @@ -2766,7 +2766,7 @@ do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx)
>      ovsdb_idl_add_table(idl, &idltest_table_simple4);
>      ovsdb_idl_add_column(idl, &idltest_simple4_col_name);
>  
> -    struct ovsdb_idl_index *index = ovsdb_idl_index_create1(
> +    struct idltest_simple3_index *index = idltest_simple3_index_create1(
>          idl, &idltest_simple3_col_uref);
>  
>      ovsdb_idl_get_initial_snapshot(idl);
> @@ -2823,8 +2823,8 @@ do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx)
>  
>  static int
>  test_idl_compound_index_single_column(struct ovsdb_idl *idl,
> -                                      struct ovsdb_idl_index *s_index,
> -                                      struct ovsdb_idl_index *i_index)
> +                                      struct idltest_simple_index *s_index,
> +                                      struct idltest_simple_index *i_index)
>  {
>      const struct idltest_simple *myRow;
>      struct ovsdb_idl_txn *txn;
> @@ -2915,10 +2915,10 @@ test_idl_compound_index_single_column(struct ovsdb_idl *idl,
>  }
>  
>  static int
> -test_idl_compound_index_double_column(struct ovsdb_idl_index *si_index,
> -                                      struct ovsdb_idl_index *sid_index,
> -                                      struct ovsdb_idl_index *is_index,
> -                                      struct ovsdb_idl_index *ids_index)
> +test_idl_compound_index_double_column(struct idltest_simple_index *si_index,
> +                                      struct idltest_simple_index *sid_index,
> +                                      struct idltest_simple_index *is_index,
> +                                      struct idltest_simple_index *ids_index)
>  {
>      const struct idltest_simple *myRow;
>      int step = 0;
> @@ -3004,33 +3004,35 @@ do_idl_compound_index(struct ovs_cmdl_context *ctx)
>      ovsdb_idl_add_column(idl, &idltest_simple_col_r);
>      ovsdb_idl_add_column(idl, &idltest_simple_col_b);
>  
> -    struct ovsdb_idl_index *s_index
> -        = ovsdb_idl_index_create1(idl, &idltest_simple_col_s);
> +    struct idltest_simple_index *s_index
> +        = idltest_simple_index_create1(idl, &idltest_simple_col_s);
>  
> -    struct ovsdb_idl_index *i_index
> -        = ovsdb_idl_index_create1(idl, &idltest_simple_col_i);
> +    struct idltest_simple_index *i_index
> +        = idltest_simple_index_create1(idl, &idltest_simple_col_i);
>  
> -    struct ovsdb_idl_index *si_index
> -        = ovsdb_idl_index_create2(idl, &idltest_simple_col_s,
> -                                  &idltest_simple_col_i);
> +    struct idltest_simple_index *si_index
> +        = idltest_simple_index_create2(idl, &idltest_simple_col_s,
> +                                       &idltest_simple_col_i);
>  
>      const struct ovsdb_idl_index_column sid_columns[] = {
>          { .column = &idltest_simple_col_s },
>          { .column = &idltest_simple_col_i, .order = OVSDB_INDEX_DESC },
>      };
> -    struct ovsdb_idl_index *sid_index
> -        = ovsdb_idl_index_create(idl, sid_columns, ARRAY_SIZE(sid_columns));
> +    struct idltest_simple_index *sid_index
> +        = idltest_simple_index_create(idl, sid_columns,
> +                                      ARRAY_SIZE(sid_columns));
>  
> -    struct ovsdb_idl_index *is_index
> -        = ovsdb_idl_index_create2(idl, &idltest_simple_col_i,
> -                                  &idltest_simple_col_s);
> +    struct idltest_simple_index *is_index
> +        = idltest_simple_index_create2(idl, &idltest_simple_col_i,
> +                                       &idltest_simple_col_s);
>  
>      const struct ovsdb_idl_index_column ids_columns[] = {
>          { .column = &idltest_simple_col_i, .order = OVSDB_INDEX_DESC },
>          { .column = &idltest_simple_col_s },
>      };
> -    struct ovsdb_idl_index *ids_index
> -        = ovsdb_idl_index_create(idl, ids_columns, ARRAY_SIZE(sid_columns));
> +    struct idltest_simple_index *ids_index
> +        = idltest_simple_index_create(idl, ids_columns,
> +                                      ARRAY_SIZE(sid_columns));
>  
>      /* wait for replica to be updated */
>      ovsdb_idl_get_initial_snapshot(idl);
> -- 
> 2.16.1
> 


More information about the dev mailing list