On Fri, Sep 3, 2021 at 8:24 AM Mark Gray <[email protected]> wrote:
>
> 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 <[email protected]>

I think the first 2 patches of this series can be considered for 21.09.

Acked-by: Numan Siddique <[email protected]>

Numan

> ---
>  northd/automake.mk  |    2 +
>  northd/northd.c     | 1106 +------------------------------------------
>  northd/northd.h     |   42 ++
>  northd/ovn-northd.c | 1100 ++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1160 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 e9a8492880e4..4a5260701fee 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);
>      }
> @@ -13171,7 +13108,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,
> @@ -14239,8 +14176,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) &&
> @@ -14532,373 +14472,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,
> @@ -14971,7 +14544,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,
> @@ -14982,6 +14555,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());
> @@ -14994,653 +14569,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..ecee14e644af
> --- /dev/null
> +++ b/northd/ovn-northd.c
> @@ -0,0 +1,1100 @@
> +/*
> + * 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);
> +
> +    /* 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);
> +        }
> +
> +        stopwatch_stop(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
> +        stopwatch_start(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
> +        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;
> +        }
> +
> +        poll_block();
> +        if (should_service_stop()) {
> +            exiting = true;
> +        }
> +    }
> +
> +
> +    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
>
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

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

Reply via email to