[ovs-dev] [PATCH ovn v5 2/2] northd: Split northd.c

Mark Gray mark.d.gray at redhat.com
Thu Sep 16 15:49:09 UTC 2021


This commit splits northd into two parts. One part, ovn-northd.c,
is responsible for the `ovn-northd` application, and the other is
responsible for northd processing.

This takes one step towards a more modular northd code base.

Signed-off-by: Mark Gray <mark.d.gray at redhat.com>
---
 northd/automake.mk  |    2 +
 northd/northd.c     | 1106 +-----------------------------------------
 northd/northd.h     |   42 ++
 northd/ovn-northd.c | 1107 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1167 insertions(+), 1090 deletions(-)
 create mode 100644 northd/northd.h
 create mode 100644 northd/ovn-northd.c

diff --git a/northd/automake.mk b/northd/automake.mk
index 306b533a486b..35ad8c09d9ba 100644
--- a/northd/automake.mk
+++ b/northd/automake.mk
@@ -2,6 +2,8 @@
 bin_PROGRAMS += northd/ovn-northd
 northd_ovn_northd_SOURCES = \
 	northd/northd.c \
+	northd/northd.h \
+	northd/ovn-northd.c \
 	northd/ipam.c \
 	northd/ipam.h
 northd_ovn_northd_LDADD = \
diff --git a/northd/northd.c b/northd/northd.c
index 2fc8b073f5a3..688a6e4efb53 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -14,17 +14,13 @@
 
 #include <config.h>
 
-#include <getopt.h>
 #include <stdlib.h>
 #include <stdio.h>
 
 #include "bitmap.h"
-#include "command-line.h"
-#include "daemon.h"
 #include "dirs.h"
 #include "ipam.h"
 #include "openvswitch/dynamic-string.h"
-#include "fatal-signal.h"
 #include "hash.h"
 #include "hmapx.h"
 #include "openvswitch/hmap.h"
@@ -40,13 +36,12 @@
 #include "lib/ovn-util.h"
 #include "lib/lb.h"
 #include "memory.h"
-#include "ovs-numa.h"
+#include "northd.h"
 #include "lib/ovn-parallel-hmap.h"
 #include "ovn/actions.h"
 #include "ovn/features.h"
 #include "ovn/logical-fields.h"
 #include "packets.h"
-#include "openvswitch/poll-loop.h"
 #include "simap.h"
 #include "smap.h"
 #include "sset.h"
@@ -54,41 +49,12 @@
 #include "stopwatch.h"
 #include "lib/stopwatch-names.h"
 #include "stream.h"
-#include "stream-ssl.h"
 #include "timeval.h"
-#include "unixctl.h"
 #include "util.h"
 #include "uuid.h"
 #include "openvswitch/vlog.h"
 
-VLOG_DEFINE_THIS_MODULE(ovn_northd);
-
-static unixctl_cb_func ovn_northd_exit;
-static unixctl_cb_func ovn_northd_pause;
-static unixctl_cb_func ovn_northd_resume;
-static unixctl_cb_func ovn_northd_is_paused;
-static unixctl_cb_func ovn_northd_status;
-static unixctl_cb_func cluster_state_reset_cmd;
-
-struct northd_context {
-    struct ovsdb_idl *ovnnb_idl;
-    struct ovsdb_idl *ovnsb_idl;
-    struct ovsdb_idl_txn *ovnnb_txn;
-    struct ovsdb_idl_txn *ovnsb_txn;
-    struct ovsdb_idl_index *sbrec_chassis_by_name;
-    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
-    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp;
-    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
-};
-
-struct northd_state {
-    bool had_lock;
-    bool paused;
-};
-
-static const char *ovnnb_db;
-static const char *ovnsb_db;
-static const char *unixctl_path;
+VLOG_DEFINE_THIS_MODULE(northd);
 
 static bool controller_event_en;
 
@@ -110,12 +76,6 @@ static bool use_ct_inv_match = true;
 #define DEFAULT_PROBE_INTERVAL_MSEC 5000
 static int northd_probe_interval_nb = 0;
 static int northd_probe_interval_sb = 0;
-
-/* SSL options */
-static const char *ssl_private_key_file;
-static const char *ssl_certificate_file;
-static const char *ssl_ca_cert_file;
-
 #define MAX_OVN_TAGS 4096
 
 /* Pipeline stages. */
@@ -403,29 +363,6 @@ ovn_stage_to_datapath_type(enum ovn_stage stage)
     }
 }
 
-static void
-usage(void)
-{
-    printf("\
-%s: OVN northbound management daemon\n\
-usage: %s [OPTIONS]\n\
-\n\
-Options:\n\
-  --ovnnb-db=DATABASE       connect to ovn-nb database at DATABASE\n\
-                            (default: %s)\n\
-  --ovnsb-db=DATABASE       connect to ovn-sb database at DATABASE\n\
-                            (default: %s)\n\
-  --dry-run                 start in paused state (do not commit db changes)\n\
-  --unixctl=SOCKET          override default control socket name\n\
-  -h, --help                display this help message\n\
-  -o, --options             list available options\n\
-  -V, --version             display version information\n\
-", program_name, program_name, default_nb_db(), default_sb_db());
-    daemon_usage();
-    vlog_usage();
-    stream_usage("database", true, true, false);
-}
-
 struct ovn_chassis_qdisc_queues {
     struct hmap_node key_node;
     uint32_t queue_id;
@@ -4360,7 +4297,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
 static bool use_logical_dp_groups = false;
 static bool use_parallel_build = true;
 
-static struct hashrow_locks lflow_locks;
+static struct hashrow_locks *lflow_locks;
 
 /* Adds a row with the specified contents to the Logical_Flow table.
  * Version to use when locking is required.
@@ -4415,11 +4352,11 @@ ovn_lflow_add_at_with_hash(struct hmap *lflow_map, struct ovn_datapath *od,
 
     ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
     if (use_logical_dp_groups && use_parallel_build) {
-        lock_hash_row(&lflow_locks, hash);
+        lock_hash_row(lflow_locks, hash);
         lflow = do_ovn_lflow_add(lflow_map, od, hash, stage, priority, match,
                                  actions, io_port, stage_hint, where,
                                  ctrl_meter);
-        unlock_hash_row(&lflow_locks, hash);
+        unlock_hash_row(lflow_locks, hash);
     } else {
         lflow = do_ovn_lflow_add(lflow_map, od, hash, stage, priority, match,
                          actions, io_port, stage_hint, where, ctrl_meter);
@@ -4455,9 +4392,9 @@ ovn_dp_group_add_with_reference(struct ovn_lflow *lflow_ref,
     }
 
     if (use_parallel_build) {
-        lock_hash_row(&lflow_locks, hash);
+        lock_hash_row(lflow_locks, hash);
         hmapx_add(&lflow_ref->od_group, od);
-        unlock_hash_row(&lflow_locks, hash);
+        unlock_hash_row(lflow_locks, hash);
     } else {
         hmapx_add(&lflow_ref->od_group, od);
     }
@@ -13176,7 +13113,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
 
     fast_hmap_size_for(&lflows, max_seen_lflow_size);
     if (use_parallel_build) {
-        update_hashrow_locks(&lflows, &lflow_locks);
+        update_hashrow_locks(&lflows, lflow_locks);
     }
     build_lswitch_and_lrouter_flows(datapaths, ports,
                                     port_groups, &lflows, mcgroups,
@@ -14246,8 +14183,11 @@ ovnnb_db_run(struct northd_context *ctx,
     smap_destroy(&options);
 
     /* Update the probe interval. */
-    northd_probe_interval_nb = get_probe_interval(ovnnb_db, nb);
-    northd_probe_interval_sb = get_probe_interval(ovnsb_db, nb);
+    northd_probe_interval_nb = get_probe_interval(ctx->ovnnb_db, nb);
+    northd_probe_interval_sb = get_probe_interval(ctx->ovnsb_db, nb);
+
+    ovsdb_idl_set_probe_interval(ctx->ovnnb_idl, northd_probe_interval_nb);
+    ovsdb_idl_set_probe_interval(ctx->ovnsb_idl, northd_probe_interval_sb);
 
     use_parallel_build =
         (smap_get_bool(&nb->options, "use_parallel_build", false) &&
@@ -14539,373 +14479,6 @@ handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
     }
 }
 
-static struct gen_opts_map supported_dhcp_opts[] = {
-    OFFERIP,
-    DHCP_OPT_NETMASK,
-    DHCP_OPT_ROUTER,
-    DHCP_OPT_DNS_SERVER,
-    DHCP_OPT_LOG_SERVER,
-    DHCP_OPT_LPR_SERVER,
-    DHCP_OPT_SWAP_SERVER,
-    DHCP_OPT_POLICY_FILTER,
-    DHCP_OPT_ROUTER_SOLICITATION,
-    DHCP_OPT_NIS_SERVER,
-    DHCP_OPT_NTP_SERVER,
-    DHCP_OPT_SERVER_ID,
-    DHCP_OPT_TFTP_SERVER,
-    DHCP_OPT_CLASSLESS_STATIC_ROUTE,
-    DHCP_OPT_MS_CLASSLESS_STATIC_ROUTE,
-    DHCP_OPT_IP_FORWARD_ENABLE,
-    DHCP_OPT_ROUTER_DISCOVERY,
-    DHCP_OPT_ETHERNET_ENCAP,
-    DHCP_OPT_DEFAULT_TTL,
-    DHCP_OPT_TCP_TTL,
-    DHCP_OPT_MTU,
-    DHCP_OPT_LEASE_TIME,
-    DHCP_OPT_T1,
-    DHCP_OPT_T2,
-    DHCP_OPT_WPAD,
-    DHCP_OPT_BOOTFILE,
-    DHCP_OPT_PATH_PREFIX,
-    DHCP_OPT_TFTP_SERVER_ADDRESS,
-    DHCP_OPT_HOSTNAME,
-    DHCP_OPT_DOMAIN_NAME,
-    DHCP_OPT_ARP_CACHE_TIMEOUT,
-    DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
-    DHCP_OPT_DOMAIN_SEARCH_LIST,
-    DHCP_OPT_BOOTFILE_ALT,
-    DHCP_OPT_BROADCAST_ADDRESS,
-    DHCP_OPT_NETBIOS_NAME_SERVER,
-    DHCP_OPT_NETBIOS_NODE_TYPE,
-};
-
-static struct gen_opts_map supported_dhcpv6_opts[] = {
-    DHCPV6_OPT_IA_ADDR,
-    DHCPV6_OPT_SERVER_ID,
-    DHCPV6_OPT_DOMAIN_SEARCH,
-    DHCPV6_OPT_DNS_SERVER
-};
-
-static void
-check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
-{
-    struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add);
-    for (size_t i = 0; (i < sizeof(supported_dhcp_opts) /
-                            sizeof(supported_dhcp_opts[0])); i++) {
-        hmap_insert(&dhcp_opts_to_add, &supported_dhcp_opts[i].hmap_node,
-                    dhcp_opt_hash(supported_dhcp_opts[i].name));
-    }
-
-    const struct sbrec_dhcp_options *opt_row, *opt_row_next;
-    SBREC_DHCP_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
-        struct gen_opts_map *dhcp_opt =
-            dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
-        if (dhcp_opt) {
-            if (!strcmp(dhcp_opt->type, opt_row->type) &&
-                 dhcp_opt->code == opt_row->code) {
-                hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node);
-            } else {
-                sbrec_dhcp_options_delete(opt_row);
-            }
-        } else {
-            sbrec_dhcp_options_delete(opt_row);
-        }
-    }
-
-    struct gen_opts_map *opt;
-    HMAP_FOR_EACH (opt, hmap_node, &dhcp_opts_to_add) {
-        struct sbrec_dhcp_options *sbrec_dhcp_option =
-            sbrec_dhcp_options_insert(ctx->ovnsb_txn);
-        sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name);
-        sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code);
-        sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type);
-    }
-
-    hmap_destroy(&dhcp_opts_to_add);
-}
-
-static void
-check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
-{
-    struct hmap dhcpv6_opts_to_add = HMAP_INITIALIZER(&dhcpv6_opts_to_add);
-    for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
-                            sizeof(supported_dhcpv6_opts[0])); i++) {
-        hmap_insert(&dhcpv6_opts_to_add, &supported_dhcpv6_opts[i].hmap_node,
-                    dhcp_opt_hash(supported_dhcpv6_opts[i].name));
-    }
-
-    const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
-    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
-        struct gen_opts_map *dhcp_opt =
-            dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
-        if (dhcp_opt) {
-            hmap_remove(&dhcpv6_opts_to_add, &dhcp_opt->hmap_node);
-        } else {
-            sbrec_dhcpv6_options_delete(opt_row);
-        }
-    }
-
-    struct gen_opts_map *opt;
-    HMAP_FOR_EACH(opt, hmap_node, &dhcpv6_opts_to_add) {
-        struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
-            sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
-        sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
-        sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
-        sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
-    }
-
-    hmap_destroy(&dhcpv6_opts_to_add);
-}
-
-static const char *rbac_chassis_auth[] =
-    {"name"};
-static const char *rbac_chassis_update[] =
-    {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches",
-     "other_config", "transport_zones"};
-
-static const char *rbac_chassis_private_auth[] =
-    {"name"};
-static const char *rbac_chassis_private_update[] =
-    {"nb_cfg", "nb_cfg_timestamp", "chassis", "external_ids"};
-
-static const char *rbac_encap_auth[] =
-    {"chassis_name"};
-static const char *rbac_encap_update[] =
-    {"type", "options", "ip"};
-
-static const char *rbac_controller_event_auth[] =
-    {""};
-static const char *rbac_controller_event_update[] =
-    {"chassis", "event_info", "event_type", "seq_num"};
-
-
-static const char *rbac_fdb_auth[] =
-    {""};
-static const char *rbac_fdb_update[] =
-    {"dp_key", "mac", "port_key"};
-
-static const char *rbac_port_binding_auth[] =
-    {""};
-static const char *rbac_port_binding_update[] =
-    {"chassis", "encap", "up", "virtual_parent"};
-
-static const char *rbac_mac_binding_auth[] =
-    {""};
-static const char *rbac_mac_binding_update[] =
-    {"logical_port", "ip", "mac", "datapath"};
-
-static const char *rbac_svc_monitor_auth[] =
-    {""};
-static const char *rbac_svc_monitor_auth_update[] =
-    {"status"};
-static const char *rbac_igmp_group_auth[] =
-    {""};
-static const char *rbac_igmp_group_update[] =
-    {"address", "chassis", "datapath", "ports"};
-
-static struct rbac_perm_cfg {
-    const char *table;
-    const char **auth;
-    int n_auth;
-    bool insdel;
-    const char **update;
-    int n_update;
-    const struct sbrec_rbac_permission *row;
-} rbac_perm_cfg[] = {
-    {
-        .table = "Chassis",
-        .auth = rbac_chassis_auth,
-        .n_auth = ARRAY_SIZE(rbac_chassis_auth),
-        .insdel = true,
-        .update = rbac_chassis_update,
-        .n_update = ARRAY_SIZE(rbac_chassis_update),
-        .row = NULL
-    },{
-        .table = "Chassis_Private",
-        .auth = rbac_chassis_private_auth,
-        .n_auth = ARRAY_SIZE(rbac_chassis_private_auth),
-        .insdel = true,
-        .update = rbac_chassis_private_update,
-        .n_update = ARRAY_SIZE(rbac_chassis_private_update),
-        .row = NULL
-    },{
-        .table = "Controller_Event",
-        .auth = rbac_controller_event_auth,
-        .n_auth = ARRAY_SIZE(rbac_controller_event_auth),
-        .insdel = true,
-        .update = rbac_controller_event_update,
-        .n_update = ARRAY_SIZE(rbac_controller_event_update),
-        .row = NULL
-    },{
-        .table = "Encap",
-        .auth = rbac_encap_auth,
-        .n_auth = ARRAY_SIZE(rbac_encap_auth),
-        .insdel = true,
-        .update = rbac_encap_update,
-        .n_update = ARRAY_SIZE(rbac_encap_update),
-        .row = NULL
-    },{
-        .table = "FDB",
-        .auth = rbac_fdb_auth,
-        .n_auth = ARRAY_SIZE(rbac_fdb_auth),
-        .insdel = true,
-        .update = rbac_fdb_update,
-        .n_update = ARRAY_SIZE(rbac_fdb_update),
-        .row = NULL
-    },{
-        .table = "Port_Binding",
-        .auth = rbac_port_binding_auth,
-        .n_auth = ARRAY_SIZE(rbac_port_binding_auth),
-        .insdel = false,
-        .update = rbac_port_binding_update,
-        .n_update = ARRAY_SIZE(rbac_port_binding_update),
-        .row = NULL
-    },{
-        .table = "MAC_Binding",
-        .auth = rbac_mac_binding_auth,
-        .n_auth = ARRAY_SIZE(rbac_mac_binding_auth),
-        .insdel = true,
-        .update = rbac_mac_binding_update,
-        .n_update = ARRAY_SIZE(rbac_mac_binding_update),
-        .row = NULL
-    },{
-        .table = "Service_Monitor",
-        .auth = rbac_svc_monitor_auth,
-        .n_auth = ARRAY_SIZE(rbac_svc_monitor_auth),
-        .insdel = false,
-        .update = rbac_svc_monitor_auth_update,
-        .n_update = ARRAY_SIZE(rbac_svc_monitor_auth_update),
-        .row = NULL
-    },{
-        .table = "IGMP_Group",
-        .auth = rbac_igmp_group_auth,
-        .n_auth = ARRAY_SIZE(rbac_igmp_group_auth),
-        .insdel = true,
-        .update = rbac_igmp_group_update,
-        .n_update = ARRAY_SIZE(rbac_igmp_group_update),
-        .row = NULL
-    },{
-        .table = NULL,
-        .auth = NULL,
-        .n_auth = 0,
-        .insdel = false,
-        .update = NULL,
-        .n_update = 0,
-        .row = NULL
-    }
-};
-
-static bool
-ovn_rbac_validate_perm(const struct sbrec_rbac_permission *perm)
-{
-    struct rbac_perm_cfg *pcfg;
-    int i, j, n_found;
-
-    for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
-        if (!strcmp(perm->table, pcfg->table)) {
-            break;
-        }
-    }
-    if (!pcfg->table) {
-        return false;
-    }
-    if (perm->n_authorization != pcfg->n_auth ||
-        perm->n_update != pcfg->n_update) {
-        return false;
-    }
-    if (perm->insert_delete != pcfg->insdel) {
-        return false;
-    }
-    /* verify perm->authorization vs. pcfg->auth */
-    n_found = 0;
-    for (i = 0; i < pcfg->n_auth; i++) {
-        for (j = 0; j < perm->n_authorization; j++) {
-            if (!strcmp(pcfg->auth[i], perm->authorization[j])) {
-                n_found++;
-                break;
-            }
-        }
-    }
-    if (n_found != pcfg->n_auth) {
-        return false;
-    }
-
-    /* verify perm->update vs. pcfg->update */
-    n_found = 0;
-    for (i = 0; i < pcfg->n_update; i++) {
-        for (j = 0; j < perm->n_update; j++) {
-            if (!strcmp(pcfg->update[i], perm->update[j])) {
-                n_found++;
-                break;
-            }
-        }
-    }
-    if (n_found != pcfg->n_update) {
-        return false;
-    }
-
-    /* Success, db state matches expected state */
-    pcfg->row = perm;
-    return true;
-}
-
-static void
-ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
-                     struct northd_context *ctx,
-                     const struct sbrec_rbac_role *rbac_role)
-{
-    struct sbrec_rbac_permission *rbac_perm;
-
-    rbac_perm = sbrec_rbac_permission_insert(ctx->ovnsb_txn);
-    sbrec_rbac_permission_set_table(rbac_perm, pcfg->table);
-    sbrec_rbac_permission_set_authorization(rbac_perm,
-                                            pcfg->auth,
-                                            pcfg->n_auth);
-    sbrec_rbac_permission_set_insert_delete(rbac_perm, pcfg->insdel);
-    sbrec_rbac_permission_set_update(rbac_perm,
-                                     pcfg->update,
-                                     pcfg->n_update);
-    sbrec_rbac_role_update_permissions_setkey(rbac_role, pcfg->table,
-                                              rbac_perm);
-}
-
-static void
-check_and_update_rbac(struct northd_context *ctx)
-{
-    const struct sbrec_rbac_role *rbac_role = NULL;
-    const struct sbrec_rbac_permission *perm_row, *perm_next;
-    const struct sbrec_rbac_role *role_row, *role_row_next;
-    struct rbac_perm_cfg *pcfg;
-
-    for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
-        pcfg->row = NULL;
-    }
-
-    SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next, ctx->ovnsb_idl) {
-        if (!ovn_rbac_validate_perm(perm_row)) {
-            sbrec_rbac_permission_delete(perm_row);
-        }
-    }
-    SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next, ctx->ovnsb_idl) {
-        if (strcmp(role_row->name, "ovn-controller")) {
-            sbrec_rbac_role_delete(role_row);
-        } else {
-            rbac_role = role_row;
-        }
-    }
-
-    if (!rbac_role) {
-        rbac_role = sbrec_rbac_role_insert(ctx->ovnsb_txn);
-        sbrec_rbac_role_set_name(rbac_role, "ovn-controller");
-    }
-
-    for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
-        if (!pcfg->row) {
-            ovn_rbac_create_perm(pcfg, ctx, rbac_role);
-        }
-    }
-}
-
 /* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */
 static void
 update_northbound_cfg(struct northd_context *ctx,
@@ -14978,7 +14551,7 @@ ovnsb_db_run(struct northd_context *ctx,
     shash_destroy(&ha_ref_chassis_map);
 }
 
-static void
+void
 ovn_db_run(struct northd_context *ctx,
            struct ovsdb_idl_index *sbrec_chassis_by_name,
            struct ovsdb_idl_loop *ovnsb_idl_loop,
@@ -14989,6 +14562,8 @@ ovn_db_run(struct northd_context *ctx,
     ovs_list_init(&lr_list);
     hmap_init(&datapaths);
     hmap_init(&ports);
+    use_parallel_build = ctx->use_parallel_build;
+    lflow_locks = ctx->lflow_locks;
 
     int64_t start_time = time_wall_msec();
     stopwatch_start(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());
@@ -15001,653 +14576,4 @@ ovn_db_run(struct northd_context *ctx,
     stopwatch_stop(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());
     destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);
 }
-
-static void
-parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED,
-              bool *paused)
-{
-    enum {
-        OVN_DAEMON_OPTION_ENUMS,
-        VLOG_OPTION_ENUMS,
-        SSL_OPTION_ENUMS,
-        OPT_DRY_RUN,
-        OPT_DUMMY_NUMA,
-    };
-    static const struct option long_options[] = {
-        {"ovnsb-db", required_argument, NULL, 'd'},
-        {"ovnnb-db", required_argument, NULL, 'D'},
-        {"unixctl", required_argument, NULL, 'u'},
-        {"help", no_argument, NULL, 'h'},
-        {"options", no_argument, NULL, 'o'},
-        {"version", no_argument, NULL, 'V'},
-        {"dry-run", no_argument, NULL, OPT_DRY_RUN},
-        {"dummy-numa", required_argument, NULL, OPT_DUMMY_NUMA},
-        OVN_DAEMON_LONG_OPTIONS,
-        VLOG_LONG_OPTIONS,
-        STREAM_SSL_LONG_OPTIONS,
-        {NULL, 0, NULL, 0},
-    };
-    char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
-
-    for (;;) {
-        int c;
-
-        c = getopt_long(argc, argv, short_options, long_options, NULL);
-        if (c == -1) {
-            break;
-        }
 
-        switch (c) {
-        OVN_DAEMON_OPTION_HANDLERS;
-        VLOG_OPTION_HANDLERS;
-
-        case 'p':
-            ssl_private_key_file = optarg;
-            break;
-
-        case 'c':
-            ssl_certificate_file = optarg;
-            break;
-
-        case 'C':
-            ssl_ca_cert_file = optarg;
-            break;
-
-        case 'd':
-            ovnsb_db = optarg;
-            break;
-
-        case 'D':
-            ovnnb_db = optarg;
-            break;
-
-        case 'u':
-            unixctl_path = optarg;
-            break;
-
-        case 'h':
-            usage();
-            exit(EXIT_SUCCESS);
-
-        case 'o':
-            ovs_cmdl_print_options(long_options);
-            exit(EXIT_SUCCESS);
-
-        case 'V':
-            ovn_print_version(0, 0);
-            exit(EXIT_SUCCESS);
-
-        case OPT_DUMMY_NUMA:
-            ovs_numa_set_dummy(optarg);
-            break;
-
-        case OPT_DRY_RUN:
-            *paused = true;
-            break;
-
-        default:
-            break;
-        }
-    }
-
-    if (!ovnsb_db || !ovnsb_db[0]) {
-        ovnsb_db = default_sb_db();
-    }
-
-    if (!ovnnb_db || !ovnnb_db[0]) {
-        ovnnb_db = default_nb_db();
-    }
-
-    free(short_options);
-}
-
-static void
-add_column_noalert(struct ovsdb_idl *idl,
-                   const struct ovsdb_idl_column *column)
-{
-    ovsdb_idl_add_column(idl, column);
-    ovsdb_idl_omit_alert(idl, column);
-}
-
-static void
-update_ssl_config(void)
-{
-    if (ssl_private_key_file && ssl_certificate_file) {
-        stream_ssl_set_key_and_cert(ssl_private_key_file,
-                                    ssl_certificate_file);
-    }
-    if (ssl_ca_cert_file) {
-        stream_ssl_set_ca_cert_file(ssl_ca_cert_file, false);
-    }
-}
-
-int
-main(int argc, char *argv[])
-{
-    int res = EXIT_SUCCESS;
-    struct unixctl_server *unixctl;
-    int retval;
-    bool exiting;
-    struct northd_state state = {
-        .had_lock = false,
-        .paused = false
-    };
-
-    fatal_ignore_sigpipe();
-    ovs_cmdl_proctitle_init(argc, argv);
-    ovn_set_program_name(argv[0]);
-    service_start(&argc, &argv);
-    parse_options(argc, argv, &state.paused);
-
-    daemonize_start(false);
-
-    char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path);
-    retval = unixctl_server_create(abs_unixctl_path, &unixctl);
-    free(abs_unixctl_path);
-
-    if (retval) {
-        exit(EXIT_FAILURE);
-    }
-    unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
-    unixctl_command_register("pause", "", 0, 0, ovn_northd_pause, &state);
-    unixctl_command_register("resume", "", 0, 0, ovn_northd_resume, &state);
-    unixctl_command_register("is-paused", "", 0, 0, ovn_northd_is_paused,
-                             &state);
-    unixctl_command_register("status", "", 0, 0, ovn_northd_status, &state);
-
-    bool reset_ovnsb_idl_min_index = false;
-    unixctl_command_register("sb-cluster-state-reset", "", 0, 0,
-                             cluster_state_reset_cmd,
-                             &reset_ovnsb_idl_min_index);
-
-    bool reset_ovnnb_idl_min_index = false;
-    unixctl_command_register("nb-cluster-state-reset", "", 0, 0,
-                             cluster_state_reset_cmd,
-                             &reset_ovnnb_idl_min_index);
-
-    daemonize_complete();
-
-    init_hash_row_locks(&lflow_locks);
-    use_parallel_build = can_parallelize_hashes(false);
-
-    /* We want to detect (almost) all changes to the ovn-nb db. */
-    struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
-        ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
-    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,
-                         &nbrec_nb_global_col_nb_cfg_timestamp);
-    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_sb_cfg);
-    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,
-                         &nbrec_nb_global_col_sb_cfg_timestamp);
-    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_hv_cfg);
-    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,
-                         &nbrec_nb_global_col_hv_cfg_timestamp);
-
-    unixctl_command_register("nb-connection-status", "", 0, 0,
-                             ovn_conn_show, ovnnb_idl_loop.idl);
-
-    /* We want to detect only selected changes to the ovn-sb db. */
-    struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
-        ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_logical_flow_col_logical_datapath);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_logical_flow_col_logical_dp_group);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_logical_flow_col_controller_meter);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_logical_flow_col_external_ids);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl,
-                        &sbrec_table_logical_dp_group);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_logical_dp_group_col_datapaths);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_multicast_group_col_datapath);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_multicast_group_col_tunnel_key);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_datapath_binding_col_tunnel_key);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_datapath_binding_col_load_balancers);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_datapath_binding_col_external_ids);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_port_binding_col_logical_port);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_port_binding_col_tunnel_key);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_port_binding_col_parent_port);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_port_binding_col_nat_addresses);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_gateway_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_ha_chassis_group);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_virtual_parent);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_up);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_gateway_chassis_col_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_gateway_chassis_col_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_gateway_chassis_col_priority);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_gateway_chassis_col_external_ids);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_gateway_chassis_col_options);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_port_binding_col_external_ids);
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_datapath);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_ip);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_mac);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_mac_binding_col_logical_port);
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcp_options);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_code);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_type);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_name);
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcpv6_options);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_code);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_type);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_name);
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_group);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_name);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_ports);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dns);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_datapaths);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_records);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_external_ids);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_role);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_name);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_permissions);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_permission);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_rbac_permission_col_table);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_rbac_permission_col_authorization);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_rbac_permission_col_insert_delete);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_permission_col_update);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_unit);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_bands);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter_band);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_action);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_rate);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_encap);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_type);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_chassis_private_col_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_chassis_private_col_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_chassis_private_col_nb_cfg);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_chassis_private_col_nb_cfg_timestamp);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_col_chassis);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_col_priority);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_col_external_ids);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis_group);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_group_col_name);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_group_col_ha_chassis);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_group_col_external_ids);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ha_chassis_group_col_ref_chassis);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_igmp_group);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_address);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_datapath);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_ports);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ip_multicast);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_datapath);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_enabled);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_querier);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_eth_src);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_ip4_src);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_ip6_src);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_table_size);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_idle_timeout);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_query_interval);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_ip_multicast_col_query_max_resp);
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_service_monitor);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_ip);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_logical_port);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_port);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_options);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_status);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_protocol);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_src_mac);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_src_ip);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_service_monitor_col_external_ids);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_load_balancer);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_datapaths);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_name);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_vips);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_protocol);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_options);
-    add_column_noalert(ovnsb_idl_loop.idl,
-                       &sbrec_load_balancer_col_external_ids);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_bfd);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_logical_port);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_dst_ip);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_status);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_tx);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_rx);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_detect_mult);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_disc);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_src_port);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_fdb);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_mac);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_dp_key);
-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_port_key);
-
-    struct ovsdb_idl_index *sbrec_chassis_by_name
-        = chassis_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name
-        = ha_chassis_group_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp
-        = mcast_group_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp
-        = ip_mcast_index_create(ovnsb_idl_loop.idl);
-
-    unixctl_command_register("sb-connection-status", "", 0, 0,
-                             ovn_conn_show, ovnsb_idl_loop.idl);
-
-    char *ovn_internal_version = ovn_get_internal_version();
-    VLOG_INFO("OVN internal version is : [%s]", ovn_internal_version);
-
-    stopwatch_create(NORTHD_LOOP_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(OVNNB_DB_RUN_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(OVNSB_DB_RUN_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(BUILD_LFLOWS_CTX_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(BUILD_LFLOWS_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(LFLOWS_DATAPATHS_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(LFLOWS_PORTS_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(LFLOWS_LBS_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(LFLOWS_IGMP_STOPWATCH_NAME, SW_MS);
-    stopwatch_create(LFLOWS_DP_GROUPS_STOPWATCH_NAME, SW_MS);
-
-    /* Main loop. */
-    exiting = false;
-
-    while (!exiting) {
-        update_ssl_config();
-        memory_run();
-        if (memory_should_report()) {
-            struct simap usage = SIMAP_INITIALIZER(&usage);
-
-            /* Nothing special to report yet. */
-            memory_report(&usage);
-            simap_destroy(&usage);
-        }
-
-        if (!state.paused) {
-            if (!ovsdb_idl_has_lock(ovnsb_idl_loop.idl) &&
-                !ovsdb_idl_is_lock_contended(ovnsb_idl_loop.idl))
-            {
-                /* Ensure that only a single ovn-northd is active in the
-                 * deployment by acquiring a lock called "ovn_northd" on the
-                 * southbound database and then only performing DB transactions
-                 * if the lock is held.
-                 */
-                ovsdb_idl_set_lock(ovnsb_idl_loop.idl, "ovn_northd");
-            }
-
-            struct northd_context ctx = {
-                .ovnnb_idl = ovnnb_idl_loop.idl,
-                .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
-                .ovnsb_idl = ovnsb_idl_loop.idl,
-                .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
-                .sbrec_chassis_by_name = sbrec_chassis_by_name,
-                .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name,
-                .sbrec_mcast_group_by_name_dp = sbrec_mcast_group_by_name_dp,
-                .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp,
-            };
-
-            if (!state.had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
-                VLOG_INFO("ovn-northd lock acquired. "
-                        "This ovn-northd instance is now active.");
-                state.had_lock = true;
-            } else if (state.had_lock &&
-                       !ovsdb_idl_has_lock(ovnsb_idl_loop.idl))
-            {
-                VLOG_INFO("ovn-northd lock lost. "
-                        "This ovn-northd instance is now on standby.");
-                state.had_lock = false;
-            }
-
-            if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
-                ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop,
-                           ovn_internal_version);
-                if (ctx.ovnsb_txn) {
-                    check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
-                    check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
-                    check_and_update_rbac(&ctx);
-                }
-            }
-
-            ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
-            ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
-        } else {
-            /* ovn-northd is paused
-             *    - we still want to handle any db updates and update the
-             *      local IDL. Otherwise, when it is resumed, the local IDL
-             *      copy will be out of sync.
-             *    - but we don't want to create any txns.
-             * */
-            if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl) ||
-                ovsdb_idl_is_lock_contended(ovnsb_idl_loop.idl))
-            {
-                /* make sure we don't hold the lock while paused */
-                VLOG_INFO("This ovn-northd instance is now paused.");
-                ovsdb_idl_set_lock(ovnsb_idl_loop.idl, NULL);
-                state.had_lock = false;
-            }
-
-            ovsdb_idl_run(ovnnb_idl_loop.idl);
-            ovsdb_idl_run(ovnsb_idl_loop.idl);
-            ovsdb_idl_wait(ovnnb_idl_loop.idl);
-            ovsdb_idl_wait(ovnsb_idl_loop.idl);
-        }
-
-        unixctl_server_run(unixctl);
-        unixctl_server_wait(unixctl);
-        memory_wait();
-        if (exiting) {
-            poll_immediate_wake();
-        }
-
-
-        ovsdb_idl_set_probe_interval(ovnnb_idl_loop.idl,
-                                     northd_probe_interval_nb);
-        ovsdb_idl_set_probe_interval(ovnsb_idl_loop.idl,
-                                     northd_probe_interval_sb);
-
-        if (reset_ovnsb_idl_min_index) {
-            VLOG_INFO("Resetting southbound database cluster state");
-            ovsdb_idl_reset_min_index(ovnsb_idl_loop.idl);
-            reset_ovnsb_idl_min_index = false;
-        }
-
-        if (reset_ovnnb_idl_min_index) {
-            VLOG_INFO("Resetting northbound database cluster state");
-            ovsdb_idl_reset_min_index(ovnnb_idl_loop.idl);
-            reset_ovnnb_idl_min_index = false;
-        }
-
-        stopwatch_stop(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
-        poll_block();
-        if (should_service_stop()) {
-            exiting = true;
-        }
-        stopwatch_start(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
-    }
-
-
-    free(ovn_internal_version);
-    unixctl_server_destroy(unixctl);
-    ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
-    ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
-    service_stop();
-
-    exit(res);
-}
-
-static void
-ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                const char *argv[] OVS_UNUSED, void *exiting_)
-{
-    bool *exiting = exiting_;
-    *exiting = true;
-
-    unixctl_command_reply(conn, NULL);
-}
-
-static void
-ovn_northd_pause(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                const char *argv[] OVS_UNUSED, void *state_)
-{
-    struct northd_state  *state = state_;
-    state->paused = true;
-
-    unixctl_command_reply(conn, NULL);
-}
-
-static void
-ovn_northd_resume(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                  const char *argv[] OVS_UNUSED, void *state_)
-{
-    struct northd_state *state = state_;
-    state->paused = false;
-
-    unixctl_command_reply(conn, NULL);
-}
-
-static void
-ovn_northd_is_paused(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                     const char *argv[] OVS_UNUSED, void *state_)
-{
-    struct northd_state *state = state_;
-    if (state->paused) {
-        unixctl_command_reply(conn, "true");
-    } else {
-        unixctl_command_reply(conn, "false");
-    }
-}
-
-static void
-ovn_northd_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                  const char *argv[] OVS_UNUSED, void *state_)
-{
-    struct northd_state *state = state_;
-    char *status;
-
-    if (state->paused) {
-        status = "paused";
-    } else {
-        status = state->had_lock ? "active" : "standby";
-    }
-
-    /*
-     * Use a labelled formatted output so we can add more to the status command
-     * later without breaking any consuming scripts
-     */
-    struct ds s = DS_EMPTY_INITIALIZER;
-    ds_put_format(&s, "Status: %s\n", status);
-    unixctl_command_reply(conn, ds_cstr(&s));
-    ds_destroy(&s);
-}
-
-static void
-cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
-               const char *argv[] OVS_UNUSED, void *idl_reset_)
-{
-    bool *idl_reset = idl_reset_;
-
-    *idl_reset = true;
-    poll_immediate_wake();
-    unixctl_command_reply(conn, NULL);
-}
diff --git a/northd/northd.h b/northd/northd.h
new file mode 100644
index 000000000000..3209d4224803
--- /dev/null
+++ b/northd/northd.h
@@ -0,0 +1,42 @@
+/*
+ * 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 NORTHD_H
+#define NORTHD_H 1
+
+#include "ovsdb-idl.h"
+#include "lib/ovn-parallel-hmap.h"
+
+struct northd_context {
+    const char *ovnnb_db;
+    const char *ovnsb_db;
+    struct ovsdb_idl *ovnnb_idl;
+    struct ovsdb_idl *ovnsb_idl;
+    struct ovsdb_idl_txn *ovnnb_txn;
+    struct ovsdb_idl_txn *ovnsb_txn;
+    struct ovsdb_idl_index *sbrec_chassis_by_name;
+    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
+    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp;
+    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
+
+    bool use_parallel_build;
+    struct hashrow_locks *lflow_locks;
+};
+
+void
+ovn_db_run(struct northd_context *ctx,
+           struct ovsdb_idl_index *sbrec_chassis_by_name,
+           struct ovsdb_idl_loop *ovnsb_idl_loop,
+           const char *ovn_internal_version);
+
+#endif /* NORTHD_H */
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
new file mode 100644
index 000000000000..ecdc64852577
--- /dev/null
+++ b/northd/ovn-northd.c
@@ -0,0 +1,1107 @@
+/*
+ * 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 <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib/chassis-index.h"
+#include "command-line.h"
+#include "daemon.h"
+#include "fatal-signal.h"
+#include "lib/ip-mcast-index.h"
+#include "lib/mcast-group-index.h"
+#include "memory.h"
+#include "northd.h"
+#include "ovs-numa.h"
+#include "ovsdb-idl.h"
+#include "lib/ovn-l7.h"
+#include "lib/ovn-nb-idl.h"
+#include "lib/ovn-parallel-hmap.h"
+#include "lib/ovn-sb-idl.h"
+#include "openvswitch/poll-loop.h"
+#include "simap.h"
+#include "stopwatch.h"
+#include "lib/stopwatch-names.h"
+#include "stream.h"
+#include "stream-ssl.h"
+#include "unixctl.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ovn_northd);
+
+static unixctl_cb_func ovn_northd_exit;
+static unixctl_cb_func ovn_northd_pause;
+static unixctl_cb_func ovn_northd_resume;
+static unixctl_cb_func ovn_northd_is_paused;
+static unixctl_cb_func ovn_northd_status;
+static unixctl_cb_func cluster_state_reset_cmd;
+
+struct northd_state {
+    bool had_lock;
+    bool paused;
+};
+
+static const char *ovnnb_db;
+static const char *ovnsb_db;
+static const char *unixctl_path;
+
+/* SSL options */
+static const char *ssl_private_key_file;
+static const char *ssl_certificate_file;
+static const char *ssl_ca_cert_file;
+
+static bool use_parallel_build = true;
+static struct hashrow_locks lflow_locks;
+
+static const char *rbac_chassis_auth[] =
+    {"name"};
+static const char *rbac_chassis_update[] =
+    {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches",
+     "other_config", "transport_zones"};
+
+static const char *rbac_chassis_private_auth[] =
+    {"name"};
+static const char *rbac_chassis_private_update[] =
+    {"nb_cfg", "nb_cfg_timestamp", "chassis", "external_ids"};
+
+static const char *rbac_encap_auth[] =
+    {"chassis_name"};
+static const char *rbac_encap_update[] =
+    {"type", "options", "ip"};
+
+static const char *rbac_controller_event_auth[] =
+    {""};
+static const char *rbac_controller_event_update[] =
+    {"chassis", "event_info", "event_type", "seq_num"};
+
+
+static const char *rbac_fdb_auth[] =
+    {""};
+static const char *rbac_fdb_update[] =
+    {"dp_key", "mac", "port_key"};
+
+static const char *rbac_port_binding_auth[] =
+    {""};
+static const char *rbac_port_binding_update[] =
+    {"chassis", "encap", "up", "virtual_parent"};
+
+static const char *rbac_mac_binding_auth[] =
+    {""};
+static const char *rbac_mac_binding_update[] =
+    {"logical_port", "ip", "mac", "datapath"};
+
+static const char *rbac_svc_monitor_auth[] =
+    {""};
+static const char *rbac_svc_monitor_auth_update[] =
+    {"status"};
+static const char *rbac_igmp_group_auth[] =
+    {""};
+static const char *rbac_igmp_group_update[] =
+    {"address", "chassis", "datapath", "ports"};
+
+static struct rbac_perm_cfg {
+    const char *table;
+    const char **auth;
+    int n_auth;
+    bool insdel;
+    const char **update;
+    int n_update;
+    const struct sbrec_rbac_permission *row;
+} rbac_perm_cfg[] = {
+    {
+        .table = "Chassis",
+        .auth = rbac_chassis_auth,
+        .n_auth = ARRAY_SIZE(rbac_chassis_auth),
+        .insdel = true,
+        .update = rbac_chassis_update,
+        .n_update = ARRAY_SIZE(rbac_chassis_update),
+        .row = NULL
+    },{
+        .table = "Chassis_Private",
+        .auth = rbac_chassis_private_auth,
+        .n_auth = ARRAY_SIZE(rbac_chassis_private_auth),
+        .insdel = true,
+        .update = rbac_chassis_private_update,
+        .n_update = ARRAY_SIZE(rbac_chassis_private_update),
+        .row = NULL
+    },{
+        .table = "Controller_Event",
+        .auth = rbac_controller_event_auth,
+        .n_auth = ARRAY_SIZE(rbac_controller_event_auth),
+        .insdel = true,
+        .update = rbac_controller_event_update,
+        .n_update = ARRAY_SIZE(rbac_controller_event_update),
+        .row = NULL
+    },{
+        .table = "Encap",
+        .auth = rbac_encap_auth,
+        .n_auth = ARRAY_SIZE(rbac_encap_auth),
+        .insdel = true,
+        .update = rbac_encap_update,
+        .n_update = ARRAY_SIZE(rbac_encap_update),
+        .row = NULL
+    },{
+        .table = "FDB",
+        .auth = rbac_fdb_auth,
+        .n_auth = ARRAY_SIZE(rbac_fdb_auth),
+        .insdel = true,
+        .update = rbac_fdb_update,
+        .n_update = ARRAY_SIZE(rbac_fdb_update),
+        .row = NULL
+    },{
+        .table = "Port_Binding",
+        .auth = rbac_port_binding_auth,
+        .n_auth = ARRAY_SIZE(rbac_port_binding_auth),
+        .insdel = false,
+        .update = rbac_port_binding_update,
+        .n_update = ARRAY_SIZE(rbac_port_binding_update),
+        .row = NULL
+    },{
+        .table = "MAC_Binding",
+        .auth = rbac_mac_binding_auth,
+        .n_auth = ARRAY_SIZE(rbac_mac_binding_auth),
+        .insdel = true,
+        .update = rbac_mac_binding_update,
+        .n_update = ARRAY_SIZE(rbac_mac_binding_update),
+        .row = NULL
+    },{
+        .table = "Service_Monitor",
+        .auth = rbac_svc_monitor_auth,
+        .n_auth = ARRAY_SIZE(rbac_svc_monitor_auth),
+        .insdel = false,
+        .update = rbac_svc_monitor_auth_update,
+        .n_update = ARRAY_SIZE(rbac_svc_monitor_auth_update),
+        .row = NULL
+    },{
+        .table = "IGMP_Group",
+        .auth = rbac_igmp_group_auth,
+        .n_auth = ARRAY_SIZE(rbac_igmp_group_auth),
+        .insdel = true,
+        .update = rbac_igmp_group_update,
+        .n_update = ARRAY_SIZE(rbac_igmp_group_update),
+        .row = NULL
+    },{
+        .table = NULL,
+        .auth = NULL,
+        .n_auth = 0,
+        .insdel = false,
+        .update = NULL,
+        .n_update = 0,
+        .row = NULL
+    }
+};
+
+static struct gen_opts_map supported_dhcp_opts[] = {
+    OFFERIP,
+    DHCP_OPT_NETMASK,
+    DHCP_OPT_ROUTER,
+    DHCP_OPT_DNS_SERVER,
+    DHCP_OPT_LOG_SERVER,
+    DHCP_OPT_LPR_SERVER,
+    DHCP_OPT_SWAP_SERVER,
+    DHCP_OPT_POLICY_FILTER,
+    DHCP_OPT_ROUTER_SOLICITATION,
+    DHCP_OPT_NIS_SERVER,
+    DHCP_OPT_NTP_SERVER,
+    DHCP_OPT_SERVER_ID,
+    DHCP_OPT_TFTP_SERVER,
+    DHCP_OPT_CLASSLESS_STATIC_ROUTE,
+    DHCP_OPT_MS_CLASSLESS_STATIC_ROUTE,
+    DHCP_OPT_IP_FORWARD_ENABLE,
+    DHCP_OPT_ROUTER_DISCOVERY,
+    DHCP_OPT_ETHERNET_ENCAP,
+    DHCP_OPT_DEFAULT_TTL,
+    DHCP_OPT_TCP_TTL,
+    DHCP_OPT_MTU,
+    DHCP_OPT_LEASE_TIME,
+    DHCP_OPT_T1,
+    DHCP_OPT_T2,
+    DHCP_OPT_WPAD,
+    DHCP_OPT_BOOTFILE,
+    DHCP_OPT_PATH_PREFIX,
+    DHCP_OPT_TFTP_SERVER_ADDRESS,
+    DHCP_OPT_HOSTNAME,
+    DHCP_OPT_DOMAIN_NAME,
+    DHCP_OPT_ARP_CACHE_TIMEOUT,
+    DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
+    DHCP_OPT_DOMAIN_SEARCH_LIST,
+    DHCP_OPT_BOOTFILE_ALT,
+    DHCP_OPT_BROADCAST_ADDRESS,
+    DHCP_OPT_NETBIOS_NAME_SERVER,
+    DHCP_OPT_NETBIOS_NODE_TYPE,
+};
+
+static struct gen_opts_map supported_dhcpv6_opts[] = {
+    DHCPV6_OPT_IA_ADDR,
+    DHCPV6_OPT_SERVER_ID,
+    DHCPV6_OPT_DOMAIN_SEARCH,
+    DHCPV6_OPT_DNS_SERVER
+};
+
+static bool
+ovn_rbac_validate_perm(const struct sbrec_rbac_permission *perm)
+{
+    struct rbac_perm_cfg *pcfg;
+    int i, j, n_found;
+
+    for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
+        if (!strcmp(perm->table, pcfg->table)) {
+            break;
+        }
+    }
+    if (!pcfg->table) {
+        return false;
+    }
+    if (perm->n_authorization != pcfg->n_auth ||
+        perm->n_update != pcfg->n_update) {
+        return false;
+    }
+    if (perm->insert_delete != pcfg->insdel) {
+        return false;
+    }
+    /* verify perm->authorization vs. pcfg->auth */
+    n_found = 0;
+    for (i = 0; i < pcfg->n_auth; i++) {
+        for (j = 0; j < perm->n_authorization; j++) {
+            if (!strcmp(pcfg->auth[i], perm->authorization[j])) {
+                n_found++;
+                break;
+            }
+        }
+    }
+    if (n_found != pcfg->n_auth) {
+        return false;
+    }
+
+    /* verify perm->update vs. pcfg->update */
+    n_found = 0;
+    for (i = 0; i < pcfg->n_update; i++) {
+        for (j = 0; j < perm->n_update; j++) {
+            if (!strcmp(pcfg->update[i], perm->update[j])) {
+                n_found++;
+                break;
+            }
+        }
+    }
+    if (n_found != pcfg->n_update) {
+        return false;
+    }
+
+    /* Success, db state matches expected state */
+    pcfg->row = perm;
+    return true;
+}
+
+static void
+ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
+                     struct northd_context *ctx,
+                     const struct sbrec_rbac_role *rbac_role)
+{
+    struct sbrec_rbac_permission *rbac_perm;
+
+    rbac_perm = sbrec_rbac_permission_insert(ctx->ovnsb_txn);
+    sbrec_rbac_permission_set_table(rbac_perm, pcfg->table);
+    sbrec_rbac_permission_set_authorization(rbac_perm,
+                                            pcfg->auth,
+                                            pcfg->n_auth);
+    sbrec_rbac_permission_set_insert_delete(rbac_perm, pcfg->insdel);
+    sbrec_rbac_permission_set_update(rbac_perm,
+                                     pcfg->update,
+                                     pcfg->n_update);
+    sbrec_rbac_role_update_permissions_setkey(rbac_role, pcfg->table,
+                                              rbac_perm);
+}
+
+static void
+check_and_update_rbac(struct northd_context *ctx)
+{
+    const struct sbrec_rbac_role *rbac_role = NULL;
+    const struct sbrec_rbac_permission *perm_row, *perm_next;
+    const struct sbrec_rbac_role *role_row, *role_row_next;
+    struct rbac_perm_cfg *pcfg;
+
+    for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
+        pcfg->row = NULL;
+    }
+
+    SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next, ctx->ovnsb_idl) {
+        if (!ovn_rbac_validate_perm(perm_row)) {
+            sbrec_rbac_permission_delete(perm_row);
+        }
+    }
+    SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next, ctx->ovnsb_idl) {
+        if (strcmp(role_row->name, "ovn-controller")) {
+            sbrec_rbac_role_delete(role_row);
+        } else {
+            rbac_role = role_row;
+        }
+    }
+
+    if (!rbac_role) {
+        rbac_role = sbrec_rbac_role_insert(ctx->ovnsb_txn);
+        sbrec_rbac_role_set_name(rbac_role, "ovn-controller");
+    }
+
+    for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
+        if (!pcfg->row) {
+            ovn_rbac_create_perm(pcfg, ctx, rbac_role);
+        }
+    }
+}
+
+static void
+check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
+{
+    struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add);
+    for (size_t i = 0; (i < sizeof(supported_dhcp_opts) /
+                            sizeof(supported_dhcp_opts[0])); i++) {
+        hmap_insert(&dhcp_opts_to_add, &supported_dhcp_opts[i].hmap_node,
+                    dhcp_opt_hash(supported_dhcp_opts[i].name));
+    }
+
+    const struct sbrec_dhcp_options *opt_row, *opt_row_next;
+    SBREC_DHCP_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
+        struct gen_opts_map *dhcp_opt =
+            dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
+        if (dhcp_opt) {
+            if (!strcmp(dhcp_opt->type, opt_row->type) &&
+                 dhcp_opt->code == opt_row->code) {
+                hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node);
+            } else {
+                sbrec_dhcp_options_delete(opt_row);
+            }
+        } else {
+            sbrec_dhcp_options_delete(opt_row);
+        }
+    }
+
+    struct gen_opts_map *opt;
+    HMAP_FOR_EACH (opt, hmap_node, &dhcp_opts_to_add) {
+        struct sbrec_dhcp_options *sbrec_dhcp_option =
+            sbrec_dhcp_options_insert(ctx->ovnsb_txn);
+        sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name);
+        sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code);
+        sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type);
+    }
+
+    hmap_destroy(&dhcp_opts_to_add);
+}
+
+static void
+check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
+{
+    struct hmap dhcpv6_opts_to_add = HMAP_INITIALIZER(&dhcpv6_opts_to_add);
+    for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
+                            sizeof(supported_dhcpv6_opts[0])); i++) {
+        hmap_insert(&dhcpv6_opts_to_add, &supported_dhcpv6_opts[i].hmap_node,
+                    dhcp_opt_hash(supported_dhcpv6_opts[i].name));
+    }
+
+    const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
+    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
+        struct gen_opts_map *dhcp_opt =
+            dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
+        if (dhcp_opt) {
+            hmap_remove(&dhcpv6_opts_to_add, &dhcp_opt->hmap_node);
+        } else {
+            sbrec_dhcpv6_options_delete(opt_row);
+        }
+    }
+
+    struct gen_opts_map *opt;
+    HMAP_FOR_EACH(opt, hmap_node, &dhcpv6_opts_to_add) {
+        struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
+            sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
+        sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
+        sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
+        sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
+    }
+
+    hmap_destroy(&dhcpv6_opts_to_add);
+}
+
+static void
+usage(void)
+{
+    printf("\
+%s: OVN northbound management daemon\n\
+usage: %s [OPTIONS]\n\
+\n\
+Options:\n\
+  --ovnnb-db=DATABASE       connect to ovn-nb database at DATABASE\n\
+                            (default: %s)\n\
+  --ovnsb-db=DATABASE       connect to ovn-sb database at DATABASE\n\
+                            (default: %s)\n\
+  --dry-run                 start in paused state (do not commit db changes)\n\
+  --unixctl=SOCKET          override default control socket name\n\
+  -h, --help                display this help message\n\
+  -o, --options             list available options\n\
+  -V, --version             display version information\n\
+", program_name, program_name, default_nb_db(), default_sb_db());
+    daemon_usage();
+    vlog_usage();
+    stream_usage("database", true, true, false);
+}
+
+static void
+parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED,
+              bool *paused)
+{
+    enum {
+        OVN_DAEMON_OPTION_ENUMS,
+        VLOG_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
+        OPT_DRY_RUN,
+        OPT_DUMMY_NUMA,
+    };
+    static const struct option long_options[] = {
+        {"ovnsb-db", required_argument, NULL, 'd'},
+        {"ovnnb-db", required_argument, NULL, 'D'},
+        {"unixctl", required_argument, NULL, 'u'},
+        {"help", no_argument, NULL, 'h'},
+        {"options", no_argument, NULL, 'o'},
+        {"version", no_argument, NULL, 'V'},
+        {"dry-run", no_argument, NULL, OPT_DRY_RUN},
+        {"dummy-numa", required_argument, NULL, OPT_DUMMY_NUMA},
+        OVN_DAEMON_LONG_OPTIONS,
+        VLOG_LONG_OPTIONS,
+        STREAM_SSL_LONG_OPTIONS,
+        {NULL, 0, NULL, 0},
+    };
+    char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
+
+    for (;;) {
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        OVN_DAEMON_OPTION_HANDLERS;
+        VLOG_OPTION_HANDLERS;
+
+        case 'p':
+            ssl_private_key_file = optarg;
+            break;
+
+        case 'c':
+            ssl_certificate_file = optarg;
+            break;
+
+        case 'C':
+            ssl_ca_cert_file = optarg;
+            break;
+
+        case 'd':
+            ovnsb_db = optarg;
+            break;
+
+        case 'D':
+            ovnnb_db = optarg;
+            break;
+
+        case 'u':
+            unixctl_path = optarg;
+            break;
+
+        case 'h':
+            usage();
+            exit(EXIT_SUCCESS);
+
+        case 'o':
+            ovs_cmdl_print_options(long_options);
+            exit(EXIT_SUCCESS);
+
+        case 'V':
+            ovn_print_version(0, 0);
+            exit(EXIT_SUCCESS);
+
+        case OPT_DUMMY_NUMA:
+            ovs_numa_set_dummy(optarg);
+            break;
+
+        case OPT_DRY_RUN:
+            *paused = true;
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    if (!ovnsb_db || !ovnsb_db[0]) {
+        ovnsb_db = default_sb_db();
+    }
+
+    if (!ovnnb_db || !ovnnb_db[0]) {
+        ovnnb_db = default_nb_db();
+    }
+
+    free(short_options);
+}
+
+static void
+add_column_noalert(struct ovsdb_idl *idl,
+                   const struct ovsdb_idl_column *column)
+{
+    ovsdb_idl_add_column(idl, column);
+    ovsdb_idl_omit_alert(idl, column);
+}
+
+static void
+update_ssl_config(void)
+{
+    if (ssl_private_key_file && ssl_certificate_file) {
+        stream_ssl_set_key_and_cert(ssl_private_key_file,
+                                    ssl_certificate_file);
+    }
+    if (ssl_ca_cert_file) {
+        stream_ssl_set_ca_cert_file(ssl_ca_cert_file, false);
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+    int res = EXIT_SUCCESS;
+    struct unixctl_server *unixctl;
+    int retval;
+    bool exiting;
+    struct northd_state state = {
+        .had_lock = false,
+        .paused = false
+    };
+
+    fatal_ignore_sigpipe();
+    ovs_cmdl_proctitle_init(argc, argv);
+    ovn_set_program_name(argv[0]);
+    service_start(&argc, &argv);
+    parse_options(argc, argv, &state.paused);
+
+    daemonize_start(false);
+
+    char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path);
+    retval = unixctl_server_create(abs_unixctl_path, &unixctl);
+    free(abs_unixctl_path);
+
+    if (retval) {
+        exit(EXIT_FAILURE);
+    }
+    unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
+    unixctl_command_register("pause", "", 0, 0, ovn_northd_pause, &state);
+    unixctl_command_register("resume", "", 0, 0, ovn_northd_resume, &state);
+    unixctl_command_register("is-paused", "", 0, 0, ovn_northd_is_paused,
+                             &state);
+    unixctl_command_register("status", "", 0, 0, ovn_northd_status, &state);
+
+    bool reset_ovnsb_idl_min_index = false;
+    unixctl_command_register("sb-cluster-state-reset", "", 0, 0,
+                             cluster_state_reset_cmd,
+                             &reset_ovnsb_idl_min_index);
+
+    bool reset_ovnnb_idl_min_index = false;
+    unixctl_command_register("nb-cluster-state-reset", "", 0, 0,
+                             cluster_state_reset_cmd,
+                             &reset_ovnnb_idl_min_index);
+
+    daemonize_complete();
+
+    init_hash_row_locks(&lflow_locks);
+    use_parallel_build = can_parallelize_hashes(false);
+
+    /* We want to detect (almost) all changes to the ovn-nb db. */
+    struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
+        ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
+    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,
+                         &nbrec_nb_global_col_nb_cfg_timestamp);
+    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_sb_cfg);
+    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,
+                         &nbrec_nb_global_col_sb_cfg_timestamp);
+    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_hv_cfg);
+    ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,
+                         &nbrec_nb_global_col_hv_cfg_timestamp);
+
+    unixctl_command_register("nb-connection-status", "", 0, 0,
+                             ovn_conn_show, ovnnb_idl_loop.idl);
+
+    /* We want to detect only selected changes to the ovn-sb db. */
+    struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
+        ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_logical_flow_col_logical_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_logical_flow_col_logical_dp_group);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_logical_flow_col_controller_meter);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_logical_flow_col_external_ids);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl,
+                        &sbrec_table_logical_dp_group);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_logical_dp_group_col_datapaths);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_multicast_group_col_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_multicast_group_col_tunnel_key);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_datapath_binding_col_tunnel_key);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_datapath_binding_col_load_balancers);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_datapath_binding_col_external_ids);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_port_binding_col_logical_port);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_port_binding_col_tunnel_key);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_port_binding_col_parent_port);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_port_binding_col_nat_addresses);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_port_binding_col_gateway_chassis);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_port_binding_col_ha_chassis_group);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_port_binding_col_virtual_parent);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_port_binding_col_up);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_gateway_chassis_col_chassis);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_gateway_chassis_col_name);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_gateway_chassis_col_priority);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_gateway_chassis_col_external_ids);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_gateway_chassis_col_options);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_port_binding_col_external_ids);
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_ip);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_mac);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_mac_binding_col_logical_port);
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcp_options);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_code);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_type);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_name);
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcpv6_options);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_code);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_type);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_name);
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_group);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_name);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_ports);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dns);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_datapaths);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_records);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_external_ids);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_role);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_name);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_permissions);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_permission);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_rbac_permission_col_table);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_rbac_permission_col_authorization);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_rbac_permission_col_insert_delete);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_permission_col_update);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_name);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_unit);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_bands);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter_band);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_action);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_rate);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_encap);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_type);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_chassis_private_col_name);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_chassis_private_col_chassis);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_chassis_private_col_nb_cfg);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_chassis_private_col_nb_cfg_timestamp);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_col_chassis);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_col_priority);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_col_external_ids);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis_group);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_group_col_name);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_group_col_ha_chassis);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_group_col_external_ids);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ha_chassis_group_col_ref_chassis);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_igmp_group);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_address);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_datapath);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_chassis);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_ports);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ip_multicast);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_enabled);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_querier);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_eth_src);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_ip4_src);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_ip6_src);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_table_size);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_idle_timeout);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_query_interval);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_ip_multicast_col_query_max_resp);
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_service_monitor);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_ip);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_logical_port);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_port);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_options);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_service_monitor_col_status);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_protocol);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_src_mac);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_src_ip);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_service_monitor_col_external_ids);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_load_balancer);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_datapaths);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_name);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_vips);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_protocol);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_options);
+    add_column_noalert(ovnsb_idl_loop.idl,
+                       &sbrec_load_balancer_col_external_ids);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_bfd);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_logical_port);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_dst_ip);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_status);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_tx);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_rx);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_detect_mult);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_disc);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_src_port);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_fdb);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_mac);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_dp_key);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_port_key);
+
+    struct ovsdb_idl_index *sbrec_chassis_by_name
+        = chassis_index_create(ovnsb_idl_loop.idl);
+
+    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name
+        = ha_chassis_group_index_create(ovnsb_idl_loop.idl);
+
+    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp
+        = mcast_group_index_create(ovnsb_idl_loop.idl);
+
+    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp
+        = ip_mcast_index_create(ovnsb_idl_loop.idl);
+
+    unixctl_command_register("sb-connection-status", "", 0, 0,
+                             ovn_conn_show, ovnsb_idl_loop.idl);
+
+    char *ovn_internal_version = ovn_get_internal_version();
+    VLOG_INFO("OVN internal version is : [%s]", ovn_internal_version);
+
+    stopwatch_create(NORTHD_LOOP_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(OVNNB_DB_RUN_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(OVNSB_DB_RUN_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(BUILD_LFLOWS_CTX_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(BUILD_LFLOWS_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(LFLOWS_DATAPATHS_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(LFLOWS_PORTS_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(LFLOWS_LBS_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(LFLOWS_IGMP_STOPWATCH_NAME, SW_MS);
+    stopwatch_create(LFLOWS_DP_GROUPS_STOPWATCH_NAME, SW_MS);
+
+    /* Main loop. */
+    exiting = false;
+
+    while (!exiting) {
+        update_ssl_config();
+        memory_run();
+        if (memory_should_report()) {
+            struct simap usage = SIMAP_INITIALIZER(&usage);
+
+            /* Nothing special to report yet. */
+            memory_report(&usage);
+            simap_destroy(&usage);
+        }
+
+        if (!state.paused) {
+            if (!ovsdb_idl_has_lock(ovnsb_idl_loop.idl) &&
+                !ovsdb_idl_is_lock_contended(ovnsb_idl_loop.idl))
+            {
+                /* Ensure that only a single ovn-northd is active in the
+                 * deployment by acquiring a lock called "ovn_northd" on the
+                 * southbound database and then only performing DB transactions
+                 * if the lock is held.
+                 */
+                ovsdb_idl_set_lock(ovnsb_idl_loop.idl, "ovn_northd");
+            }
+
+            struct northd_context ctx = {
+                .ovnnb_db = ovnnb_db,
+                .ovnsb_db = ovnsb_db,
+                .ovnnb_idl = ovnnb_idl_loop.idl,
+                .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
+                .ovnsb_idl = ovnsb_idl_loop.idl,
+                .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
+                .sbrec_chassis_by_name = sbrec_chassis_by_name,
+                .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name,
+                .sbrec_mcast_group_by_name_dp = sbrec_mcast_group_by_name_dp,
+                .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp,
+                .lflow_locks = &lflow_locks,
+                .use_parallel_build = use_parallel_build,
+            };
+
+            if (!state.had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
+                VLOG_INFO("ovn-northd lock acquired. "
+                        "This ovn-northd instance is now active.");
+                state.had_lock = true;
+            } else if (state.had_lock &&
+                       !ovsdb_idl_has_lock(ovnsb_idl_loop.idl))
+            {
+                VLOG_INFO("ovn-northd lock lost. "
+                        "This ovn-northd instance is now on standby.");
+                state.had_lock = false;
+            }
+
+            if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
+                ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop,
+                           ovn_internal_version);
+                if (ctx.ovnsb_txn) {
+                    check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
+                    check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
+                    check_and_update_rbac(&ctx);
+                }
+            }
+
+            ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
+            ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
+        } else {
+            /* ovn-northd is paused
+             *    - we still want to handle any db updates and update the
+             *      local IDL. Otherwise, when it is resumed, the local IDL
+             *      copy will be out of sync.
+             *    - but we don't want to create any txns.
+             * */
+            if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl) ||
+                ovsdb_idl_is_lock_contended(ovnsb_idl_loop.idl))
+            {
+                /* make sure we don't hold the lock while paused */
+                VLOG_INFO("This ovn-northd instance is now paused.");
+                ovsdb_idl_set_lock(ovnsb_idl_loop.idl, NULL);
+                state.had_lock = false;
+            }
+
+            ovsdb_idl_run(ovnnb_idl_loop.idl);
+            ovsdb_idl_run(ovnsb_idl_loop.idl);
+            ovsdb_idl_wait(ovnnb_idl_loop.idl);
+            ovsdb_idl_wait(ovnsb_idl_loop.idl);
+        }
+
+        unixctl_server_run(unixctl);
+        unixctl_server_wait(unixctl);
+        memory_wait();
+        if (exiting) {
+            poll_immediate_wake();
+        }
+
+        if (reset_ovnsb_idl_min_index) {
+            VLOG_INFO("Resetting southbound database cluster state");
+            ovsdb_idl_reset_min_index(ovnsb_idl_loop.idl);
+            reset_ovnsb_idl_min_index = false;
+        }
+
+        if (reset_ovnnb_idl_min_index) {
+            VLOG_INFO("Resetting northbound database cluster state");
+            ovsdb_idl_reset_min_index(ovnnb_idl_loop.idl);
+            reset_ovnnb_idl_min_index = false;
+        }
+
+        stopwatch_stop(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
+        poll_block();
+        if (should_service_stop()) {
+            exiting = true;
+        }
+        stopwatch_start(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
+    }
+
+
+    free(ovn_internal_version);
+    unixctl_server_destroy(unixctl);
+    ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
+    ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
+    service_stop();
+
+    exit(res);
+}
+
+static void
+ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                const char *argv[] OVS_UNUSED, void *exiting_)
+{
+    bool *exiting = exiting_;
+    *exiting = true;
+
+    unixctl_command_reply(conn, NULL);
+}
+
+static void
+ovn_northd_pause(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                const char *argv[] OVS_UNUSED, void *state_)
+{
+    struct northd_state  *state = state_;
+    state->paused = true;
+
+    unixctl_command_reply(conn, NULL);
+}
+
+static void
+ovn_northd_resume(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                  const char *argv[] OVS_UNUSED, void *state_)
+{
+    struct northd_state *state = state_;
+    state->paused = false;
+
+    unixctl_command_reply(conn, NULL);
+}
+
+static void
+ovn_northd_is_paused(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                     const char *argv[] OVS_UNUSED, void *state_)
+{
+    struct northd_state *state = state_;
+    if (state->paused) {
+        unixctl_command_reply(conn, "true");
+    } else {
+        unixctl_command_reply(conn, "false");
+    }
+}
+
+static void
+ovn_northd_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                  const char *argv[] OVS_UNUSED, void *state_)
+{
+    struct northd_state *state = state_;
+    char *status;
+
+    if (state->paused) {
+        status = "paused";
+    } else {
+        status = state->had_lock ? "active" : "standby";
+    }
+
+    /*
+     * Use a labelled formatted output so we can add more to the status command
+     * later without breaking any consuming scripts
+     */
+    struct ds s = DS_EMPTY_INITIALIZER;
+    ds_put_format(&s, "Status: %s\n", status);
+    unixctl_command_reply(conn, ds_cstr(&s));
+    ds_destroy(&s);
+}
+
+static void
+cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
+               const char *argv[] OVS_UNUSED, void *idl_reset_)
+{
+    bool *idl_reset = idl_reset_;
+
+    *idl_reset = true;
+    poll_immediate_wake();
+    unixctl_command_reply(conn, NULL);
+}
-- 
2.27.0




More information about the dev mailing list