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

Mark Michelson mmichels at redhat.com
Wed Jun 27 21:09:04 UTC 2018


On 06/26/2018 10:22 PM, Han Zhou wrote:
> On Wed, Jun 20, 2018 at 11:04 PM, Ben Pfaff <blp at ovn.org> 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?
>>
> 
> Hi Ben,
> 
> Thanks for the great work and sorry for slow response.
> 
> Regarding the idl generation, the .def files define how each field of a
> record can be accessed: no access, writeonly, etc, but it didn't specify
> whether intersions or deletions are allowed on the table. Apparently
> insertions and deletions should be considered as write operations to the
> table. The insert/delete interfaces should not be generated for RO tables.
> 
> Another general comment is, this patch defines views of tables at module
> level. In the incremental processing, we probably want to define different
> engine nodes (like my earlier patches do), which may not be exact 1-1 match
> to the modules. Each node should have its own view of the data, so I am not
> sure if this module level view satisfies our goal of incremental
> processing. Do you have any thoughts on this? Or are you suggesting making
> engine nodes 1-1 mapping with modules?

I interpreted the module level views to be what fits with the current 
ovn-controller. You can think of this as an example to follow rather 
than a permanent fixture.

With incremental processing, I think you would remove some of the 
existing module-level views and replace them with views that represent 
engine nodes. Once incremental processing is fully implemented, I think 
you'd see a 1-1 mapping of views to engine nodes, rather than the 
current 1-1 mapping of views to modules.

> 
> Please find my other comments inlined.
> 
> Thanks,
> Han
> 
>> 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(
> 
> Would it be better to follow same naming convention as the original
> interface, i.e. ...init_row instead of ...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]);
> 
> I have a concern for this kind of conversion for DB records that is
> contained in other data structures. There are 3 problems:
> 
> 1. Since local_datapaths is just a hmap, how to link it to the structure of
> the hmap node when generating the dependency remains unclear to me.
> 
> 2. From the input of this function, it depends on local_datapaths, which
> contains the original version of datapath record, so the function should be
> considered as potentially writing the datapath record, no matter if the
> conversion happens here or not, while in fact this function only reads the
> record with the restricted view. So the real dependency is not reflected
> correctly from the interface of the function. (Open issue of this
> description itself: ).
> 
> 3. Looking at the how the data structure is generated, it is in
> binding_run(), where a different view of the table is used as input
> (binding_sbrec_datapath_binding), and it is then type converted to the
> generic type sbrec_datapath_binding. This also loses track of the real
> dependency.
> 
> Maybe you already have idea how to address these problems in the dependency
> auto-generation, but here is my rough idea:
> 
> When node B depends on node A, and A has ovsdb table record T as its output
> while B take T as input, it requires some coversion between B's view of T
> (TB) and A's view of T. (TA) This coversion can be performed in a generic
> way in the incremental processing engine framework, so that output of B is
> converted as input of A according to the definition of the input and the
> output data structure of engine nodes themselves.
> 
> Basically, each node has its own version of data structure, even if they in
> fact contains same data (e.g. node B's output has TB in a structure S-B,
> while node A's input has TA in a structure S-A, and S-A is converted from
> S-B. It is simply type convertion without runtime cost. This can be
> achieved with some json *template* similar as the .def files to define
> dependencies explicitely with output <-> input conversion mappings.
> (instead of auto-generate the dependency, we can simplify it with
> auto-validation, but probably you have better idea, like you always did :))
> 
> Regarding the problem 1), what I think we can do is to make sure naming
> convention is followed for parameters with type hmap, smap, etc. For
> example, struct hmap *local_datapaths indicates the type of the node is
> struct local_datapath. This doesn't prevent someone from using struct X to
> access a hmap node supposed to be struct Y, but I think it is generic
> problem even without considering the incremental processing.
> 
> There is much more details to be figured out, but it leads to the
> discussion of the implementation of dependency auto-generation mechanism.
> 
>>               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)
> 
> Auto-generating the register function is a great idea. Looking forward to
> the tracking version.
> 
>> +{\
>> +'''
>> +          % {'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
>>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> 



More information about the dev mailing list