diff mbox series

[ovs-dev,v1,02/12] ovn-br-controller: Connect to OVS database and define engine nodes.

Message ID 20250811100935.917848-1-numans@ovn.org
State Changes Requested
Headers show
Series OVN Bridge Controller service. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Numan Siddique Aug. 11, 2025, 10:09 a.m. UTC
From: Numan Siddique <numans@ovn.org>

Signed-off-by: Numan Siddique <numans@ovn.org>
---
 br-controller/automake.mk         |   6 +-
 br-controller/en-bridge-data.c    |  45 +++++
 br-controller/en-bridge-data.h    |  16 ++
 br-controller/en-lflow.c          |  43 +++++
 br-controller/en-lflow.h          |  16 ++
 br-controller/ovn-br-controller.c | 279 +++++++++++++++++++++++++++++-
 lib/inc-proc-eng.h                |  11 ++
 7 files changed, 409 insertions(+), 7 deletions(-)
 create mode 100644 br-controller/en-bridge-data.c
 create mode 100644 br-controller/en-bridge-data.h
 create mode 100644 br-controller/en-lflow.c
 create mode 100644 br-controller/en-lflow.h

Comments

Mark Michelson Aug. 21, 2025, 9:12 p.m. UTC | #1
Thanks Numan!

Acked-by: Mark Michelson <mmichels@redhat.com>

On 8/11/25 6:09 AM, numans@ovn.org wrote:
> From: Numan Siddique <numans@ovn.org>
> 
> Signed-off-by: Numan Siddique <numans@ovn.org>
> ---
>   br-controller/automake.mk         |   6 +-
>   br-controller/en-bridge-data.c    |  45 +++++
>   br-controller/en-bridge-data.h    |  16 ++
>   br-controller/en-lflow.c          |  43 +++++
>   br-controller/en-lflow.h          |  16 ++
>   br-controller/ovn-br-controller.c | 279 +++++++++++++++++++++++++++++-
>   lib/inc-proc-eng.h                |  11 ++
>   7 files changed, 409 insertions(+), 7 deletions(-)
>   create mode 100644 br-controller/en-bridge-data.c
>   create mode 100644 br-controller/en-bridge-data.h
>   create mode 100644 br-controller/en-lflow.c
>   create mode 100644 br-controller/en-lflow.h
> 
> diff --git a/br-controller/automake.mk b/br-controller/automake.mk
> index 012e9a5ed8..497f440551 100644
> --- a/br-controller/automake.mk
> +++ b/br-controller/automake.mk
> @@ -1,6 +1,10 @@
>   bin_PROGRAMS += br-controller/ovn-br-controller
>   br_controller_ovn_br_controller_SOURCES = \
> -	br-controller/ovn-br-controller.c
> +	br-controller/ovn-br-controller.c \
> +	br-controller/en-lflow.c \
> +	br-controller/en-lflow.h \
> +	br-controller/en-bridge-data.c \
> +	br-controller/en-bridge-data.h
>   
>   br_controller_ovn_br_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la
>   man_MANS += br-controller/ovn-br-controller.8
> diff --git a/br-controller/en-bridge-data.c b/br-controller/en-bridge-data.c
> new file mode 100644
> index 0000000000..293ae895c0
> --- /dev/null
> +++ b/br-controller/en-bridge-data.c
> @@ -0,0 +1,45 @@
> +/*
> + * 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>
> +
> +/* OVS includes. */
> +#include "openvswitch/vlog.h"
> +
> +/* OVN includes. */
> +#include "en-bridge-data.h"
> +
> +VLOG_DEFINE_THIS_MODULE(en_bridge_data);
> +
> +void *
> +en_bridge_data_init(struct engine_node *node OVS_UNUSED,
> +                    struct engine_arg *arg OVS_UNUSED)
> +{
> +    return NULL;
> +}
> +
> +void
> +en_bridge_data_cleanup(void *data OVS_UNUSED)
> +{
> +}
> +
> +enum engine_node_state
> +en_bridge_data_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED)
> +{
> +    return EN_UNCHANGED;
> +}
> diff --git a/br-controller/en-bridge-data.h b/br-controller/en-bridge-data.h
> new file mode 100644
> index 0000000000..4a280b5cd1
> --- /dev/null
> +++ b/br-controller/en-bridge-data.h
> @@ -0,0 +1,16 @@
> +#ifndef EN_RUNTIME_DATA_H
> +#define EN_RUNTIME_DATA_H 1
> +
> +#include <config.h>
> +
> +#include <getopt.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "lib/inc-proc-eng.h"
> +
> +enum engine_node_state en_bridge_data_run(struct engine_node *, void *data);
> +void *en_bridge_data_init(struct engine_node *node, struct engine_arg *arg);
> +void en_bridge_data_cleanup(void *data);
> +
> +#endif /* EN_RUNTIME_DATA_H */
> diff --git a/br-controller/en-lflow.c b/br-controller/en-lflow.c
> new file mode 100644
> index 0000000000..b1a6efbf5f
> --- /dev/null
> +++ b/br-controller/en-lflow.c
> @@ -0,0 +1,43 @@
> +/*
> + * 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>
> +
> +/* OVS includes. */
> +#include "openvswitch/vlog.h"
> +
> +/* OVN includes. */
> +#include "en-lflow.h"
> +
> +VLOG_DEFINE_THIS_MODULE(en_lflow);
> +
> +void *en_lflow_output_init(struct engine_node *node OVS_UNUSED,
> +                          struct engine_arg *arg OVS_UNUSED)
> +{
> +    return NULL;
> +}
> +
> +void en_lflow_output_cleanup(void *data_ OVS_UNUSED)
> +{
> +}
> +
> +enum engine_node_state
> +en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED)
> +{
> +    return EN_UNCHANGED;
> +}
> diff --git a/br-controller/en-lflow.h b/br-controller/en-lflow.h
> new file mode 100644
> index 0000000000..c3889cbdc9
> --- /dev/null
> +++ b/br-controller/en-lflow.h
> @@ -0,0 +1,16 @@
> +#ifndef EN_LFLOW_H
> +#define EN_LFLOW_H 1
> +
> +#include <config.h>
> +
> +#include <getopt.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "lib/inc-proc-eng.h"
> +
> +enum engine_node_state en_lflow_output_run(struct engine_node *, void *data);
> +void *en_lflow_output_init(struct engine_node *node, struct engine_arg *arg);
> +void en_lflow_output_cleanup(void *data);
> +
> +#endif /* EN_LFLOW_H */
> diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c
> index 0fef0e5fee..27c5e28d0c 100644
> --- a/br-controller/ovn-br-controller.c
> +++ b/br-controller/ovn-br-controller.c
> @@ -22,25 +22,30 @@
>   #include <string.h>
>   
>   /* OVS includes. */
> -#include "openvswitch/vlog.h"
>   #include "lib/command-line.h"
>   #include "lib/daemon.h"
>   #include "lib/dirs.h"
>   #include "lib/fatal-signal.h"
>   #include "lib/stream.h"
>   #include "lib/stream-ssl.h"
> +#include "lib/vswitch-idl.h"
>   #include "lib/unixctl.h"
> +#include "openvswitch/poll-loop.h"
> +#include "openvswitch/vlog.h"
> +
>   
>   /* OVN includes. */
> +#include "en-bridge-data.h"
> +#include "en-lflow.h"
>   #include "lib/ovn-br-idl.h"
> +#include "lib/inc-proc-eng.h"
>   #include "lib/ovn-util.h"
>   
>   VLOG_DEFINE_THIS_MODULE(main);
>   
> -static void parse_options(int argc, char *argv[]);
> +static char *parse_options(int argc, char *argv[]);
>   OVS_NO_RETURN static void usage(void);
>   
> -
>   /* SSL/TLS options. */
>   static const char *ssl_private_key_file;
>   static const char *ssl_certificate_file;
> @@ -49,20 +54,225 @@ static const char *ssl_ca_cert_file;
>   /* --unixctl-path: Path to use for unixctl server socket. */
>   static char *unixctl_path;
>   
> +#define BRCTL_NODES \
> +    BRCTL_NODE(br_global) \
> +    BRCTL_NODE(bridge) \
> +    BRCTL_NODE(logical_flow)
> +
> +enum brctl_engine_node {
> +#define BRCTL_NODE(NAME) BRCTL_##NAME,
> +    BRCTL_NODES
> +#undef BRCTL_NODE
> +};
> +
> +#define BRCTL_NODE(NAME) ENGINE_FUNC_BR(NAME);
> +    BRCTL_NODES
> +#undef BRCTL_NODE
> +
> +#define OVS_NODES \
> +    OVS_NODE(open_vswitch) \
> +    OVS_NODE(bridge) \
> +    OVS_NODE(port) \
> +    OVS_NODE(interface)
> +
> +enum ovs_engine_node {
> +#define OVS_NODE(NAME) OVS_##NAME,
> +    OVS_NODES
> +#undef OVS_NODE
> +};
> +
> +#define OVS_NODE(NAME) ENGINE_FUNC_OVS(NAME);
> +    OVS_NODES
> +#undef OVS_NODE
> +
> +/* Engine static functions. */
> +static void *
> +en_br_controller_output_init(struct engine_node *node OVS_UNUSED,
> +                             struct engine_arg *arg OVS_UNUSED)
> +{
> +    return NULL;
> +}
> +
> +static void
> +en_br_controller_output_cleanup(void *data OVS_UNUSED)
> +{
> +
> +}
> +
> +static enum engine_node_state
> +en_br_controller_output_run(struct engine_node *node OVS_UNUSED,
> +                            void *data OVS_UNUSED)
> +{
> +    return EN_UPDATED;
> +}
> +
> +/* Static function declarations. */
> +static void ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl);
> +static void update_br_db(struct ovsdb_idl *ovs_idl,
> +                         struct ovsdb_idl *ovn_br_idl);
> +
>   int
>   main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
>   {
> +    struct unixctl_server *unixctl;
> +    struct ovn_exit_args exit_args = {0};
> +    int retval;
> +
>       ovs_cmdl_proctitle_init(argc, argv);
>       ovn_set_program_name(argv[0]);
>       service_start(&argc, &argv);
> -    parse_options(argc, argv);
> +    char *ovs_remote = parse_options(argc, argv);
>       fatal_ignore_sigpipe();
>   
> -    return 0;
> +    daemonize_start(true, 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, 1, ovn_exit_command_callback,
> +                             &exit_args);
> +
> +    daemonize_complete();
> +
> +    /* Connect to OVS OVSDB instance. */
> +    struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
> +        ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
> +    ctrl_register_ovs_idl(ovs_idl_loop.idl);
> +
> +    ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl);
> +
> +    /* Configure OVN bridge database. */
> +    struct ovsdb_idl_loop ovnbr_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
> +        ovsdb_idl_create_unconnected(&ovnbrrec_idl_class, true));
> +    ovsdb_idl_set_leader_only(ovnbr_idl_loop.idl, false);
> +
> +    ovsdb_idl_track_add_all(ovnbr_idl_loop.idl);
> +
> +    unixctl_command_register("connection-status", "", 0, 0,
> +                             ovn_conn_show, ovnbr_idl_loop.idl);
> +
> +    /* We don't want to monitor Connection table at all. So omit all the
> +     * columns. */
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_external_ids);
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl,
> +                   &ovnbrrec_connection_col_inactivity_probe);
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_is_connected);
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_max_backoff);
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_other_config);
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_status);
> +    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_target);
> +
> +    /* Define inc-proc-engine nodes. */
> +    ENGINE_NODE(bridge_data);
> +    ENGINE_NODE(lflow_output);
> +    ENGINE_NODE(br_controller_output);
> +
> +#define BRCTL_NODE(NAME) ENGINE_NODE_BR(NAME);
> +    BRCTL_NODES
> +#undef BRCTL_NODE
> +
> +#define OVS_NODE(NAME) ENGINE_NODE_OVS(NAME);
> +    OVS_NODES
> +#undef OVS_NODE
> +
> +    engine_add_input(&en_bridge_data, &en_ovs_open_vswitch, NULL);
> +    engine_add_input(&en_bridge_data, &en_ovs_bridge, NULL);
> +    engine_add_input(&en_bridge_data, &en_ovs_port, NULL);
> +    engine_add_input(&en_bridge_data, &en_ovs_interface, NULL);
> +
> +    engine_add_input(&en_bridge_data, &en_ovnbr_br_global, NULL);
> +    engine_add_input(&en_bridge_data, &en_ovnbr_bridge, NULL);
> +
> +    engine_add_input(&en_lflow_output, &en_bridge_data, NULL);
> +    engine_add_input(&en_lflow_output, &en_ovnbr_logical_flow, NULL);
> +    engine_add_input(&en_br_controller_output, &en_lflow_output, NULL);
> +
> +    struct engine_arg engine_arg = {
> +        .ovs_idl = ovs_idl_loop.idl,
> +        .ovnbr_idl = ovnbr_idl_loop.idl,
> +    };
> +    engine_init(&en_br_controller_output, &engine_arg);
> +
> +    unsigned int ovs_cond_seqno = UINT_MAX;
> +    unsigned int ovnbr_cond_seqno = UINT_MAX;
> +
> +    /* Main loop. */
> +    while (!exit_args.exiting) {
> +        engine_init_run();
> +
> +        struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop);
> +        unsigned int new_ovs_cond_seqno
> +            = ovsdb_idl_get_condition_seqno(ovs_idl_loop.idl);
> +        if (new_ovs_cond_seqno != ovs_cond_seqno) {
> +            if (!new_ovs_cond_seqno) {
> +                VLOG_INFO("OVS IDL reconnected, force recompute.");
> +                engine_set_force_recompute();
> +            }
> +            ovs_cond_seqno = new_ovs_cond_seqno;
> +        }
> +
> +        update_br_db(ovs_idl_loop.idl, ovnbr_idl_loop.idl);
> +        struct ovsdb_idl_txn *ovnbr_idl_txn
> +            = ovsdb_idl_loop_run(&ovnbr_idl_loop);
> +        unsigned int new_ovnbr_cond_seqno
> +            = ovsdb_idl_get_condition_seqno(ovnbr_idl_loop.idl);
> +        if (new_ovnbr_cond_seqno != ovnbr_cond_seqno) {
> +            if (!new_ovnbr_cond_seqno) {
> +                VLOG_INFO("OVNBR IDL reconnected, force recompute.");
> +                engine_set_force_recompute();
> +            }
> +            ovnbr_cond_seqno = new_ovnbr_cond_seqno;
> +        }
> +
> +        struct engine_context eng_ctx = {
> +            .ovs_idl_txn = ovs_idl_txn,
> +            .ovnbr_idl_txn = ovnbr_idl_txn,
> +        };
> +
> +        engine_set_context(&eng_ctx);
> +
> +        const struct ovsrec_open_vswitch_table *ovs_table =
> +            ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
> +        const struct ovsrec_open_vswitch *cfg =
> +            ovsrec_open_vswitch_table_first(ovs_table);
> +
> +        if (ovsdb_idl_has_ever_connected(ovnbr_idl_loop.idl) && cfg) {
> +            engine_run(true);
> +        }
> +
> +        unixctl_server_run(unixctl);
> +
> +        unixctl_server_wait(unixctl);
> +        if (exit_args.exiting) {
> +            poll_immediate_wake();
> +        }
> +
> +        ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
> +        ovsdb_idl_loop_commit_and_wait(&ovnbr_idl_loop);
> +        ovsdb_idl_track_clear(ovnbr_idl_loop.idl);
> +        ovsdb_idl_track_clear(ovs_idl_loop.idl);
> +
> +        poll_block();
> +        if (should_service_stop()) {
> +            exit_args.exiting = true;
> +        }
> +    }
> +
> +    engine_set_context(NULL);
> +    engine_cleanup();
> +
> +    free(ovs_remote);
> +    ovn_exit_args_finish(&exit_args);
> +    unixctl_server_destroy(unixctl);
> +    service_stop();
> +    exit(0);
>   }
>   
>   /* static functions. */
> -static void
> +static char *
>   parse_options(int argc, char *argv[])
>   {
>       enum {
> @@ -153,6 +363,17 @@ parse_options(int argc, char *argv[])
>   
>       argc -= optind;
>       argv += optind;
> +
> +    char *ovs_remote = NULL;
> +    if (argc == 0) {
> +        ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
> +    } else if (argc == 1) {
> +        ovs_remote = xstrdup(argv[0]);
> +    } else {
> +        VLOG_FATAL("exactly zero or one non-option argument required; "
> +                   "use --help for usage");
> +    }
> +    return ovs_remote;
>   }
>   
>   static void
> @@ -173,3 +394,49 @@ usage(void)
>              "  -V, --version           display version information\n");
>       exit(EXIT_SUCCESS);
>   }
> +
> +static void
> +ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> +{
> +    /* We do not monitor all tables by default, so modules must register
> +     * their interest explicitly.
> +     * When the same column is monitored in different modes by different
> +     * modules, there is a chance that "track" flag added by
> +     * ovsdb_idl_track_add_column by one module being overwritten by a
> +     * following ovsdb_idl_add_column by another module. Before this is fixed
> +     * in OVSDB IDL, we need to be careful about the order so that the "track"
> +     * calls are after the "non-track" calls. */
> +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_other_config);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_datapaths);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
> +
> +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_bridge_col_name);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_external_ids);
> +
> +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> +
> +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
> +}
> +
> +/* Retrieves the pointer to the OVN Bridge Controller database from 'ovs_idl'
> + * and updates 'brdb_idl' with that pointer. */
> +static void
> +update_br_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnbr_idl)
> +{
> +    const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
> +    if (!cfg) {
> +        return;
> +    }
> +
> +    const char *remote = smap_get(&cfg->external_ids, "ovn-br-remote");
> +    ovsdb_idl_set_remote(ovnbr_idl, remote, true);
> +}
> diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
> index d9174effa7..5a486f86f8 100644
> --- a/lib/inc-proc-eng.h
> +++ b/lib/inc-proc-eng.h
> @@ -157,6 +157,7 @@ struct engine_context {
>       struct ovsdb_idl_txn *ovs_idl_txn;
>       struct ovsdb_idl_txn *ovnsb_idl_txn;
>       struct ovsdb_idl_txn *ovnnb_idl_txn;
> +    struct ovsdb_idl_txn *ovnbr_idl_txn;
>   
>       void *client_ctx;
>   };
> @@ -165,6 +166,7 @@ struct engine_context {
>   struct engine_arg {
>       struct ovsdb_idl *sb_idl;
>       struct ovsdb_idl *nb_idl;
> +    struct ovsdb_idl *ovnbr_idl;
>       struct ovsdb_idl *ovs_idl;
>   };
>   
> @@ -521,6 +523,11 @@ en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node)  \
>   #define ENGINE_FUNC_NB(TBL_NAME) \
>       ENGINE_FUNC_OVSDB(nb, TBL_NAME)
>   
> +/* Macro to define member functions of an engine node which represents
> + * a table of OVN BR DB */
> +#define ENGINE_FUNC_BR(TBL_NAME) \
> +    ENGINE_FUNC_OVSDB(ovnbr, TBL_NAME)
> +
>   /* Macro to define member functions of an engine node which represents
>    * a table of open_vswitch DB */
>   #define ENGINE_FUNC_OVS(TBL_NAME) \
> @@ -540,6 +547,10 @@ en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node)  \
>   #define ENGINE_NODE_NB(TBL_NAME) \
>       ENGINE_NODE_OVSDB(nb, "NB", TBL_NAME, #TBL_NAME);
>   
> +/* Macro to define an engine node which represents a table of OVN BR DB */
> +#define ENGINE_NODE_BR(TBL_NAME) \
> +    ENGINE_NODE_OVSDB(ovnbr, "BR", TBL_NAME, #TBL_NAME);
> +
>   /* Macro to define an engine node which represents a table of open_vswitch
>    * DB */
>   #define ENGINE_NODE_OVS(TBL_NAME) \
diff mbox series

Patch

diff --git a/br-controller/automake.mk b/br-controller/automake.mk
index 012e9a5ed8..497f440551 100644
--- a/br-controller/automake.mk
+++ b/br-controller/automake.mk
@@ -1,6 +1,10 @@ 
 bin_PROGRAMS += br-controller/ovn-br-controller
 br_controller_ovn_br_controller_SOURCES = \
-	br-controller/ovn-br-controller.c
+	br-controller/ovn-br-controller.c \
+	br-controller/en-lflow.c \
+	br-controller/en-lflow.h \
+	br-controller/en-bridge-data.c \
+	br-controller/en-bridge-data.h
 
 br_controller_ovn_br_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la
 man_MANS += br-controller/ovn-br-controller.8
diff --git a/br-controller/en-bridge-data.c b/br-controller/en-bridge-data.c
new file mode 100644
index 0000000000..293ae895c0
--- /dev/null
+++ b/br-controller/en-bridge-data.c
@@ -0,0 +1,45 @@ 
+/*
+ * 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>
+
+/* OVS includes. */
+#include "openvswitch/vlog.h"
+
+/* OVN includes. */
+#include "en-bridge-data.h"
+
+VLOG_DEFINE_THIS_MODULE(en_bridge_data);
+
+void *
+en_bridge_data_init(struct engine_node *node OVS_UNUSED,
+                    struct engine_arg *arg OVS_UNUSED)
+{
+    return NULL;
+}
+
+void
+en_bridge_data_cleanup(void *data OVS_UNUSED)
+{
+}
+
+enum engine_node_state
+en_bridge_data_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED)
+{
+    return EN_UNCHANGED;
+}
diff --git a/br-controller/en-bridge-data.h b/br-controller/en-bridge-data.h
new file mode 100644
index 0000000000..4a280b5cd1
--- /dev/null
+++ b/br-controller/en-bridge-data.h
@@ -0,0 +1,16 @@ 
+#ifndef EN_RUNTIME_DATA_H
+#define EN_RUNTIME_DATA_H 1
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib/inc-proc-eng.h"
+
+enum engine_node_state en_bridge_data_run(struct engine_node *, void *data);
+void *en_bridge_data_init(struct engine_node *node, struct engine_arg *arg);
+void en_bridge_data_cleanup(void *data);
+
+#endif /* EN_RUNTIME_DATA_H */
diff --git a/br-controller/en-lflow.c b/br-controller/en-lflow.c
new file mode 100644
index 0000000000..b1a6efbf5f
--- /dev/null
+++ b/br-controller/en-lflow.c
@@ -0,0 +1,43 @@ 
+/*
+ * 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>
+
+/* OVS includes. */
+#include "openvswitch/vlog.h"
+
+/* OVN includes. */
+#include "en-lflow.h"
+
+VLOG_DEFINE_THIS_MODULE(en_lflow);
+
+void *en_lflow_output_init(struct engine_node *node OVS_UNUSED,
+                          struct engine_arg *arg OVS_UNUSED)
+{
+    return NULL;
+}
+
+void en_lflow_output_cleanup(void *data_ OVS_UNUSED)
+{
+}
+
+enum engine_node_state
+en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED)
+{
+    return EN_UNCHANGED;
+}
diff --git a/br-controller/en-lflow.h b/br-controller/en-lflow.h
new file mode 100644
index 0000000000..c3889cbdc9
--- /dev/null
+++ b/br-controller/en-lflow.h
@@ -0,0 +1,16 @@ 
+#ifndef EN_LFLOW_H
+#define EN_LFLOW_H 1
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib/inc-proc-eng.h"
+
+enum engine_node_state en_lflow_output_run(struct engine_node *, void *data);
+void *en_lflow_output_init(struct engine_node *node, struct engine_arg *arg);
+void en_lflow_output_cleanup(void *data);
+
+#endif /* EN_LFLOW_H */
diff --git a/br-controller/ovn-br-controller.c b/br-controller/ovn-br-controller.c
index 0fef0e5fee..27c5e28d0c 100644
--- a/br-controller/ovn-br-controller.c
+++ b/br-controller/ovn-br-controller.c
@@ -22,25 +22,30 @@ 
 #include <string.h>
 
 /* OVS includes. */
-#include "openvswitch/vlog.h"
 #include "lib/command-line.h"
 #include "lib/daemon.h"
 #include "lib/dirs.h"
 #include "lib/fatal-signal.h"
 #include "lib/stream.h"
 #include "lib/stream-ssl.h"
+#include "lib/vswitch-idl.h"
 #include "lib/unixctl.h"
+#include "openvswitch/poll-loop.h"
+#include "openvswitch/vlog.h"
+
 
 /* OVN includes. */
+#include "en-bridge-data.h"
+#include "en-lflow.h"
 #include "lib/ovn-br-idl.h"
+#include "lib/inc-proc-eng.h"
 #include "lib/ovn-util.h"
 
 VLOG_DEFINE_THIS_MODULE(main);
 
-static void parse_options(int argc, char *argv[]);
+static char *parse_options(int argc, char *argv[]);
 OVS_NO_RETURN static void usage(void);
 
-
 /* SSL/TLS options. */
 static const char *ssl_private_key_file;
 static const char *ssl_certificate_file;
@@ -49,20 +54,225 @@  static const char *ssl_ca_cert_file;
 /* --unixctl-path: Path to use for unixctl server socket. */
 static char *unixctl_path;
 
+#define BRCTL_NODES \
+    BRCTL_NODE(br_global) \
+    BRCTL_NODE(bridge) \
+    BRCTL_NODE(logical_flow)
+
+enum brctl_engine_node {
+#define BRCTL_NODE(NAME) BRCTL_##NAME,
+    BRCTL_NODES
+#undef BRCTL_NODE
+};
+
+#define BRCTL_NODE(NAME) ENGINE_FUNC_BR(NAME);
+    BRCTL_NODES
+#undef BRCTL_NODE
+
+#define OVS_NODES \
+    OVS_NODE(open_vswitch) \
+    OVS_NODE(bridge) \
+    OVS_NODE(port) \
+    OVS_NODE(interface)
+
+enum ovs_engine_node {
+#define OVS_NODE(NAME) OVS_##NAME,
+    OVS_NODES
+#undef OVS_NODE
+};
+
+#define OVS_NODE(NAME) ENGINE_FUNC_OVS(NAME);
+    OVS_NODES
+#undef OVS_NODE
+
+/* Engine static functions. */
+static void *
+en_br_controller_output_init(struct engine_node *node OVS_UNUSED,
+                             struct engine_arg *arg OVS_UNUSED)
+{
+    return NULL;
+}
+
+static void
+en_br_controller_output_cleanup(void *data OVS_UNUSED)
+{
+
+}
+
+static enum engine_node_state
+en_br_controller_output_run(struct engine_node *node OVS_UNUSED,
+                            void *data OVS_UNUSED)
+{
+    return EN_UPDATED;
+}
+
+/* Static function declarations. */
+static void ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl);
+static void update_br_db(struct ovsdb_idl *ovs_idl,
+                         struct ovsdb_idl *ovn_br_idl);
+
 int
 main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
+    struct unixctl_server *unixctl;
+    struct ovn_exit_args exit_args = {0};
+    int retval;
+
     ovs_cmdl_proctitle_init(argc, argv);
     ovn_set_program_name(argv[0]);
     service_start(&argc, &argv);
-    parse_options(argc, argv);
+    char *ovs_remote = parse_options(argc, argv);
     fatal_ignore_sigpipe();
 
-    return 0;
+    daemonize_start(true, 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, 1, ovn_exit_command_callback,
+                             &exit_args);
+
+    daemonize_complete();
+
+    /* Connect to OVS OVSDB instance. */
+    struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
+        ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
+    ctrl_register_ovs_idl(ovs_idl_loop.idl);
+
+    ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl);
+
+    /* Configure OVN bridge database. */
+    struct ovsdb_idl_loop ovnbr_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
+        ovsdb_idl_create_unconnected(&ovnbrrec_idl_class, true));
+    ovsdb_idl_set_leader_only(ovnbr_idl_loop.idl, false);
+
+    ovsdb_idl_track_add_all(ovnbr_idl_loop.idl);
+
+    unixctl_command_register("connection-status", "", 0, 0,
+                             ovn_conn_show, ovnbr_idl_loop.idl);
+
+    /* We don't want to monitor Connection table at all. So omit all the
+     * columns. */
+    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_external_ids);
+    ovsdb_idl_omit(ovnbr_idl_loop.idl,
+                   &ovnbrrec_connection_col_inactivity_probe);
+    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_is_connected);
+    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_max_backoff);
+    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_other_config);
+    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_status);
+    ovsdb_idl_omit(ovnbr_idl_loop.idl, &ovnbrrec_connection_col_target);
+
+    /* Define inc-proc-engine nodes. */
+    ENGINE_NODE(bridge_data);
+    ENGINE_NODE(lflow_output);
+    ENGINE_NODE(br_controller_output);
+
+#define BRCTL_NODE(NAME) ENGINE_NODE_BR(NAME);
+    BRCTL_NODES
+#undef BRCTL_NODE
+
+#define OVS_NODE(NAME) ENGINE_NODE_OVS(NAME);
+    OVS_NODES
+#undef OVS_NODE
+
+    engine_add_input(&en_bridge_data, &en_ovs_open_vswitch, NULL);
+    engine_add_input(&en_bridge_data, &en_ovs_bridge, NULL);
+    engine_add_input(&en_bridge_data, &en_ovs_port, NULL);
+    engine_add_input(&en_bridge_data, &en_ovs_interface, NULL);
+
+    engine_add_input(&en_bridge_data, &en_ovnbr_br_global, NULL);
+    engine_add_input(&en_bridge_data, &en_ovnbr_bridge, NULL);
+
+    engine_add_input(&en_lflow_output, &en_bridge_data, NULL);
+    engine_add_input(&en_lflow_output, &en_ovnbr_logical_flow, NULL);
+    engine_add_input(&en_br_controller_output, &en_lflow_output, NULL);
+
+    struct engine_arg engine_arg = {
+        .ovs_idl = ovs_idl_loop.idl,
+        .ovnbr_idl = ovnbr_idl_loop.idl,
+    };
+    engine_init(&en_br_controller_output, &engine_arg);
+
+    unsigned int ovs_cond_seqno = UINT_MAX;
+    unsigned int ovnbr_cond_seqno = UINT_MAX;
+
+    /* Main loop. */
+    while (!exit_args.exiting) {
+        engine_init_run();
+
+        struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop);
+        unsigned int new_ovs_cond_seqno
+            = ovsdb_idl_get_condition_seqno(ovs_idl_loop.idl);
+        if (new_ovs_cond_seqno != ovs_cond_seqno) {
+            if (!new_ovs_cond_seqno) {
+                VLOG_INFO("OVS IDL reconnected, force recompute.");
+                engine_set_force_recompute();
+            }
+            ovs_cond_seqno = new_ovs_cond_seqno;
+        }
+
+        update_br_db(ovs_idl_loop.idl, ovnbr_idl_loop.idl);
+        struct ovsdb_idl_txn *ovnbr_idl_txn
+            = ovsdb_idl_loop_run(&ovnbr_idl_loop);
+        unsigned int new_ovnbr_cond_seqno
+            = ovsdb_idl_get_condition_seqno(ovnbr_idl_loop.idl);
+        if (new_ovnbr_cond_seqno != ovnbr_cond_seqno) {
+            if (!new_ovnbr_cond_seqno) {
+                VLOG_INFO("OVNBR IDL reconnected, force recompute.");
+                engine_set_force_recompute();
+            }
+            ovnbr_cond_seqno = new_ovnbr_cond_seqno;
+        }
+
+        struct engine_context eng_ctx = {
+            .ovs_idl_txn = ovs_idl_txn,
+            .ovnbr_idl_txn = ovnbr_idl_txn,
+        };
+
+        engine_set_context(&eng_ctx);
+
+        const struct ovsrec_open_vswitch_table *ovs_table =
+            ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
+        const struct ovsrec_open_vswitch *cfg =
+            ovsrec_open_vswitch_table_first(ovs_table);
+
+        if (ovsdb_idl_has_ever_connected(ovnbr_idl_loop.idl) && cfg) {
+            engine_run(true);
+        }
+
+        unixctl_server_run(unixctl);
+
+        unixctl_server_wait(unixctl);
+        if (exit_args.exiting) {
+            poll_immediate_wake();
+        }
+
+        ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
+        ovsdb_idl_loop_commit_and_wait(&ovnbr_idl_loop);
+        ovsdb_idl_track_clear(ovnbr_idl_loop.idl);
+        ovsdb_idl_track_clear(ovs_idl_loop.idl);
+
+        poll_block();
+        if (should_service_stop()) {
+            exit_args.exiting = true;
+        }
+    }
+
+    engine_set_context(NULL);
+    engine_cleanup();
+
+    free(ovs_remote);
+    ovn_exit_args_finish(&exit_args);
+    unixctl_server_destroy(unixctl);
+    service_stop();
+    exit(0);
 }
 
 /* static functions. */
-static void
+static char *
 parse_options(int argc, char *argv[])
 {
     enum {
@@ -153,6 +363,17 @@  parse_options(int argc, char *argv[])
 
     argc -= optind;
     argv += optind;
+
+    char *ovs_remote = NULL;
+    if (argc == 0) {
+        ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
+    } else if (argc == 1) {
+        ovs_remote = xstrdup(argv[0]);
+    } else {
+        VLOG_FATAL("exactly zero or one non-option argument required; "
+                   "use --help for usage");
+    }
+    return ovs_remote;
 }
 
 static void
@@ -173,3 +394,49 @@  usage(void)
            "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
 }
+
+static void
+ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
+{
+    /* We do not monitor all tables by default, so modules must register
+     * their interest explicitly.
+     * When the same column is monitored in different modes by different
+     * modules, there is a chance that "track" flag added by
+     * ovsdb_idl_track_add_column by one module being overwritten by a
+     * following ovsdb_idl_add_column by another module. Before this is fixed
+     * in OVSDB IDL, we need to be careful about the order so that the "track"
+     * calls are after the "non-track" calls. */
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_other_config);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_datapaths);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
+
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_bridge_col_name);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_external_ids);
+
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
+
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
+    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
+}
+
+/* Retrieves the pointer to the OVN Bridge Controller database from 'ovs_idl'
+ * and updates 'brdb_idl' with that pointer. */
+static void
+update_br_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnbr_idl)
+{
+    const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
+    if (!cfg) {
+        return;
+    }
+
+    const char *remote = smap_get(&cfg->external_ids, "ovn-br-remote");
+    ovsdb_idl_set_remote(ovnbr_idl, remote, true);
+}
diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
index d9174effa7..5a486f86f8 100644
--- a/lib/inc-proc-eng.h
+++ b/lib/inc-proc-eng.h
@@ -157,6 +157,7 @@  struct engine_context {
     struct ovsdb_idl_txn *ovs_idl_txn;
     struct ovsdb_idl_txn *ovnsb_idl_txn;
     struct ovsdb_idl_txn *ovnnb_idl_txn;
+    struct ovsdb_idl_txn *ovnbr_idl_txn;
 
     void *client_ctx;
 };
@@ -165,6 +166,7 @@  struct engine_context {
 struct engine_arg {
     struct ovsdb_idl *sb_idl;
     struct ovsdb_idl *nb_idl;
+    struct ovsdb_idl *ovnbr_idl;
     struct ovsdb_idl *ovs_idl;
 };
 
@@ -521,6 +523,11 @@  en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node)  \
 #define ENGINE_FUNC_NB(TBL_NAME) \
     ENGINE_FUNC_OVSDB(nb, TBL_NAME)
 
+/* Macro to define member functions of an engine node which represents
+ * a table of OVN BR DB */
+#define ENGINE_FUNC_BR(TBL_NAME) \
+    ENGINE_FUNC_OVSDB(ovnbr, TBL_NAME)
+
 /* Macro to define member functions of an engine node which represents
  * a table of open_vswitch DB */
 #define ENGINE_FUNC_OVS(TBL_NAME) \
@@ -540,6 +547,10 @@  en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node)  \
 #define ENGINE_NODE_NB(TBL_NAME) \
     ENGINE_NODE_OVSDB(nb, "NB", TBL_NAME, #TBL_NAME);
 
+/* Macro to define an engine node which represents a table of OVN BR DB */
+#define ENGINE_NODE_BR(TBL_NAME) \
+    ENGINE_NODE_OVSDB(ovnbr, "BR", TBL_NAME, #TBL_NAME);
+
 /* Macro to define an engine node which represents a table of open_vswitch
  * DB */
 #define ENGINE_NODE_OVS(TBL_NAME) \