On Wed, Nov 5, 2025 at 6:49 PM Han Zhou <[email protected]> wrote:
>
>
>
> On Mon, Nov 3, 2025 at 6:18 PM <[email protected]> wrote:
> >
> > From: Numan Siddique <[email protected]>
> >
> > This commit is the first of the series to add a new service
> > to OVN called ovn-br-controller (OVN bridge controller).
> > This commit adds
> >  - a schema file
>
> Thanks Numan for implementing this!
> For the DB access, is there a consideration if this is used by different CMS, 
> each managing different OVS bridges, how to avoid stepping over the flow 
> records belonging to different components? Is it reasonable to add RBAC 
> support? This may be added as enhancement in the future, if possible at all, 
> but probably you already have thoughts on this.

Hi Han,
Thanks for the review comments.  I agree with you. I think it's
reasonable to add RBAC support.
I haven't thought of it in detail as there have been requirements for
us to have multiple CMS/agents manage different OVS bridges.
I think we should definitely consider it as a future support.

>
> >  - a basic skeleton of ovn-br-controller service (which does nothing)
> >  - ovn-brctl utility similar to ovn-nbctl/ovn-sbctl.
> >
> > Rational for adding this service:
>
> nit: s/Rational/Rationale

Ack.

>
> > There's a need to configure the provider bridges with specific OpenFlow
> > rules after packets leave the OVN pipeline and enters these provider
> > bridges  via the patch ports.  Since OVN has a very good abstraction
> > of the OpenFlow rules using OVN logical flows, we can leverage the same
> > here so that CMS doesn't have to deal with the specifics of OpenFlow
> > protocol.  Also if the CMS is written in golang or other languages,
> > CMS has to mostly rely on ovs-vsctl/ovs-ofctl to program the flows.
> > Adding this support in OVN avoids the CMS to exec these utils to
> > add/delete and dump the existing OpenFlow rules.
> >
> > Any user can also use this new service to program and manage any OVS
> > bridge without using OVN and hence this service is named as
> > "ovn-br-controller."
> >
> > This commit also adds ovn-br-controller sub package for fedora/rhel.
> >
> > Signed-off-by: Numan Siddique <[email protected]>
> > ---
> >  Makefile.am                                   |   5 +-
> >  automake.mk                                   |  36 ++
> >  br-controller/automake.mk                     |   8 +
> >  br-controller/ovn-br-controller.8.xml         |  24 +
> >  br-controller/ovn-br-controller.c             | 175 ++++++
> >  lib/automake.mk                               |  17 +-
> >  lib/ovn-br-idl.ann                            |   9 +
> >  lib/ovn-util.c                                |  31 ++
> >  lib/ovn-util.h                                |  16 +
> >  ovn-br.ovsschema                              |  94 ++++
> >  ovn-br.xml                                    | 452 +++++++++++++++
> >  rhel/automake.mk                              |   4 +-
> >  rhel/ovn-fedora.spec.in                       |  22 +-
> >  ...b_systemd_system_ovn-br-controller.service |  35 ++
> >  rhel/usr_lib_systemd_system_ovn-br-db.service |  32 ++
> >  tutorial/ovn-sandbox                          |  24 +-
> >  utilities/automake.mk                         |   8 +
> >  utilities/ovn-brctl.c                         | 524 ++++++++++++++++++
> >  utilities/ovn-sbctl.c                         |  30 -
> >  19 files changed, 1511 insertions(+), 35 deletions(-)
> >  create mode 100644 br-controller/automake.mk
> >  create mode 100644 br-controller/ovn-br-controller.8.xml
> >  create mode 100644 br-controller/ovn-br-controller.c
> >  create mode 100644 lib/ovn-br-idl.ann
> >  create mode 100644 ovn-br.ovsschema
> >  create mode 100644 ovn-br.xml
> >  create mode 100644 rhel/usr_lib_systemd_system_ovn-br-controller.service
> >  create mode 100644 rhel/usr_lib_systemd_system_ovn-br-db.service
> >  create mode 100644 utilities/ovn-brctl.c
> >
> > diff --git a/Makefile.am b/Makefile.am
> > index 4ea4ae6c52..3ad2077b37 100644
> > --- a/Makefile.am
> > +++ b/Makefile.am
> > @@ -115,7 +115,9 @@ EXTRA_DIST = \
> >         ovn-ic-nb.ovsschema \
> >         ovn-ic-nb.xml \
> >         ovn-ic-sb.ovsschema \
> > -       ovn-ic-sb.xml
> > +       ovn-ic-sb.xml \
> > +       ovn-br.ovsschema \
> > +       ovn-br.xml
> >  bin_PROGRAMS =
> >  sbin_PROGRAMS =
> >  bin_SCRIPTS =
> > @@ -512,4 +514,5 @@ include controller/automake.mk
> >  include controller-vtep/automake.mk
> >  include northd/automake.mk
> >  include ic/automake.mk
> > +include br-controller/automake.mk
> >  include build-aux/automake.mk
> > diff --git a/automake.mk b/automake.mk
> > index a7947a3f54..53860ad89b 100644
> > --- a/automake.mk
> > +++ b/automake.mk
> > @@ -120,6 +120,35 @@ ovn-ic-sb.5: \
> >                 $(srcdir)/ovn-ic-sb.xml > [email protected] && \
> >         mv [email protected] $@
> >
> > +# OVN bridge controller E-R diagram
> > +#
> > +# If "python" or "dot" is not available, then we do not add graphical 
> > diagram
> > +# to the documentation.
> > +if HAVE_DOT
> > +ovn-br.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-br.ovsschema
> > +       $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-br.ovsschema > $@
> > +ovn-br.pic: ovn-br.gv ${OVSDIR}/ovsdb/dot2pic
> > +       $(AM_V_GEN)(dot -T plain < ovn-br.gv | $(PYTHON3) 
> > ${OVSDIR}/ovsdb/dot2pic -f 3) > [email protected] && \
> > +       mv [email protected] $@
> > +OVN_BR_PIC = ovn-br.pic
> > +OVN_BR_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_BR_PIC)
> > +CLEANFILES += ovn-br.gv ovn-br.pic
> > +endif
> > +
> > +# OVN bridge controller schema documentation
> > +EXTRA_DIST += ovn-br.xml
> > +CLEANFILES += ovn-br.5
> > +man_MANS += ovn-br.5
> > +
> > +ovn-br.5: \
> > +       ${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-br.xml 
> > $(srcdir)/ovn-br.ovsschema $(OVN_BR_PIC)
> > +       $(AM_V_GEN)$(OVSDB_DOC) \
> > +               $(OVN_BR_DOT_DIAGRAM_ARG) \
> > +               --version=$(VERSION) \
> > +               $(srcdir)/ovn-br.ovsschema \
> > +               $(srcdir)/ovn-br.xml > [email protected] && \
> > +       mv [email protected] $@
> > +
> >  # Version checking for ovn-nb.ovsschema.
> >  ALL_LOCAL += ovn-nb.ovsschema.stamp
> >  ovn-nb.ovsschema.stamp: ovn-nb.ovsschema
> > @@ -143,9 +172,16 @@ ovn-ic-sb.ovsschema.stamp: ovn-ic-sb.ovsschema
> >         $(srcdir)/build-aux/cksum-schema-check $? $@
> >  CLEANFILES += ovn-ic-sb.ovsschema.stamp
> >
> > +# Version checking for ovn-br.ovsschema.
> > +ALL_LOCAL += ovn-br.ovsschema.stamp
> > +ovn-br.ovsschema.stamp: ovn-br.ovsschema
> > +       $(srcdir)/build-aux/cksum-schema-check $? $@
> > +CLEANFILES += ovn-br.ovsschema.stamp
> > +
> >  pkgdata_DATA += ovn-nb.ovsschema
> >  pkgdata_DATA += ovn-sb.ovsschema
> >  pkgdata_DATA += ovn-ic-nb.ovsschema
> >  pkgdata_DATA += ovn-ic-sb.ovsschema
> > +pkgdata_DATA += ovn-br.ovsschema
> >
> >  CLEANFILES += ovn-sb.ovsschema.stamp
> > diff --git a/br-controller/automake.mk b/br-controller/automake.mk
> > new file mode 100644
> > index 0000000000..012e9a5ed8
> > --- /dev/null
> > +++ b/br-controller/automake.mk
> > @@ -0,0 +1,8 @@
> > +bin_PROGRAMS += br-controller/ovn-br-controller
> > +br_controller_ovn_br_controller_SOURCES = \
> > +       br-controller/ovn-br-controller.c
> > +
> > +br_controller_ovn_br_controller_LDADD = lib/libovn.la 
> > $(OVS_LIBDIR)/libopenvswitch.la
> > +man_MANS += br-controller/ovn-br-controller.8
> > +EXTRA_DIST += br-controller/ovn-br-controller.8.xml
> > +CLEANFILES += br-controller/ovn-br-controller.8
> > diff --git a/br-controller/ovn-br-controller.8.xml 
> > b/br-controller/ovn-br-controller.8.xml
> > new file mode 100644
> > index 0000000000..be81bd9188
> > --- /dev/null
> > +++ b/br-controller/ovn-br-controller.8.xml
> > @@ -0,0 +1,24 @@
> > +<?xml version="1.0" encoding="utf-8"?>
> > +<manpage program="ovn-br-controller" section="8" title="ovn-br-controller">
> > +    <h1>Name</h1>
> > +    <p>ovn-br-controller -- Open Virtual Network local OVS bridge 
> > controller</p>
> > +
> > +    <h1>Synopsis</h1>
> > +    <p>
> > +      <code>ovn-br-controller</code> [<var>options</var>]
> > +      [<var>ovs-database</var>]
> > +    </p>
> > +
> > +    <h1>Description</h1>
> > +    <p>
> > +      <code>ovn-br-controller</code> is OVN logical flow based
> > +      local controller daemon to control and program the Open vSwitch
> > +      bridges.  It connects up to the OVN bridge database (see 
> > <code>ovn-br</code>(5))
> > +      over the OVSDB protocol, and down to the Open vSwitch database (see
> > +      <code>ovs-vswitchd.conf.db</code>(5)) over the OVSDB protocol and
> > +      to <code>ovs-vswitchd</code>(8) via OpenFlow.  
> > <code>ovn-br-controller</code>
> > +      processes and converts the OVN Logical flows configured in the OVN 
> > bridge database
> > +      for the Open vSwitch bridges to OpenFlow rules and programs these 
> > flow rules to the
> > +      Open vSwitch bridges.
> > +    </p>
> > +</manpage>
> > diff --git a/br-controller/ovn-br-controller.c 
> > b/br-controller/ovn-br-controller.c
> > new file mode 100644
> > index 0000000000..0fef0e5fee
> > --- /dev/null
> > +++ b/br-controller/ovn-br-controller.c
> > @@ -0,0 +1,175 @@
> > +/* Copyright (c) 2025 Crusoe Energy Systems LLC
> > + *
> > + * 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 <errno.h>
> > +#include <getopt.h>
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +#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/unixctl.h"
> > +
> > +/* OVN includes. */
> > +#include "lib/ovn-br-idl.h"
> > +#include "lib/ovn-util.h"
> > +
> > +VLOG_DEFINE_THIS_MODULE(main);
> > +
> > +static void 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;
> > +static const char *ssl_ca_cert_file;
> > +
> > +/* --unixctl-path: Path to use for unixctl server socket. */
> > +static char *unixctl_path;
> > +
> > +int
> > +main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
> > +{
> > +    ovs_cmdl_proctitle_init(argc, argv);
> > +    ovn_set_program_name(argv[0]);
> > +    service_start(&argc, &argv);
> > +    parse_options(argc, argv);
> > +    fatal_ignore_sigpipe();
> > +
> > +    return 0;
> > +}
> > +
> > +/* static functions. */
> > +static void
> > +parse_options(int argc, char *argv[])
> > +{
> > +    enum {
> > +        OPT_PEER_CA_CERT = UCHAR_MAX + 1,
> > +        OPT_BOOTSTRAP_CA_CERT,
> > +        VLOG_OPTION_ENUMS,
> > +        OVN_DAEMON_OPTION_ENUMS,
> > +        SSL_OPTION_ENUMS,
> > +    };
> > +
> > +    static struct option long_options[] = {
> > +        {"help", no_argument, NULL, 'h'},
> > +        {"version", no_argument, NULL, 'V'},
> > +        {"unixctl", required_argument, NULL, 'u'},
> > +        VLOG_LONG_OPTIONS,
> > +        OVN_DAEMON_LONG_OPTIONS,
> > +        STREAM_SSL_LONG_OPTIONS,
> > +        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
> > +        {"bootstrap-ca-cert", required_argument, NULL, 
> > OPT_BOOTSTRAP_CA_CERT},
> > +        {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) {
> > +        case 'h':
> > +            usage();
> > +
> > +        case 'V':
> > +            ovs_print_version(OFP15_VERSION, OFP15_VERSION);
> > +            printf("OVN BR DB Schema %s\n", ovnbrrec_get_db_version());
> > +            exit(EXIT_SUCCESS);
> > +
> > +        case 'u':
> > +            unixctl_path = optarg;
> > +            break;
> > +
> > +        VLOG_OPTION_HANDLERS
> > +        OVN_DAEMON_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 OPT_SSL_PROTOCOLS:
> > +            stream_ssl_set_protocols(optarg);
> > +            break;
> > +
> > +        case OPT_SSL_CIPHERS:
> > +            stream_ssl_set_ciphers(optarg);
> > +            break;
> > +
> > +        case OPT_SSL_CIPHERSUITES:
> > +            stream_ssl_set_ciphersuites(optarg);
> > +            break;
> > +
> > +        case OPT_PEER_CA_CERT:
> > +            stream_ssl_set_peer_ca_cert_file(optarg);
> > +            break;
> > +
> > +        case OPT_BOOTSTRAP_CA_CERT:
> > +            stream_ssl_set_ca_cert_file(optarg, true);
> > +            break;
> > +
> > +        case '?':
> > +            exit(EXIT_FAILURE);
> > +
> > +        default:
> > +            ovs_abort(0, "Invalid option.");
> > +        }
> > +    }
> > +    free(short_options);
> > +
> > +    argc -= optind;
> > +    argv += optind;
> > +}
> > +
> > +static void
> > +usage(void)
> > +{
> > +    printf("%s: OVN bridge controller\n"
> > +           "usage %s [OPTIONS] [OVS-DATABASE]\n"
> > +           "where OVS-DATABASE is a socket on which the OVS OVSDB server "
> > +           "is listening.\n",
> > +           program_name, program_name);
> > +    stream_usage("OVS-DATABASE", true, false, true);
> > +    daemon_usage();
> > +    vlog_usage();
> > +    printf("\nOther options:\n"
> > +           "  -u, --unixctl=SOCKET    set control socket name\n"
> > +           "  -n                      custom chassis name\n"
> > +           "  -h, --help              display this help message\n"
> > +           "  -V, --version           display version information\n");
> > +    exit(EXIT_SUCCESS);
> > +}
> > diff --git a/lib/automake.mk b/lib/automake.mk
> > index a59c722d62..46c24e99d1 100644
> > --- a/lib/automake.mk
> > +++ b/lib/automake.mk
> > @@ -59,7 +59,9 @@ nodist_lib_libovn_la_SOURCES = \
> >         lib/ovn-ic-nb-idl.c \
> >         lib/ovn-ic-nb-idl.h \
> >         lib/ovn-ic-sb-idl.c \
> > -       lib/ovn-ic-sb-idl.h
> > +       lib/ovn-ic-sb-idl.h \
> > +       lib/ovn-br-idl.c \
> > +       lib/ovn-br-idl.h
> >
> >  CLEANFILES += $(nodist_lib_libovn_la_SOURCES)
> >
> > @@ -129,3 +131,16 @@ OVN_IC_SB_IDL_FILES = \
> >  lib/ovn-ic-sb-idl.ovsidl: $(OVN_IC_SB_IDL_FILES)
> >         $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_IC_SB_IDL_FILES) > [email protected] 
> > && \
> >         mv [email protected] $@
> > +
> > +# ovn-br IDL
> > +OVSIDL_BUILT += \
> > +       lib/ovn-br-idl.c \
> > +       lib/ovn-br-idl.h \
> > +       lib/ovn-br-idl.ovsidl
> > +EXTRA_DIST += lib/ovn-br-idl.ann
> > +OVN_PR_IDL_FILES = \
> > +       $(srcdir)/ovn-br.ovsschema \
> > +       $(srcdir)/lib/ovn-br-idl.ann
> > +lib/ovn-br-idl.ovsidl: $(OVN_PR_IDL_FILES)
> > +       $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_PR_IDL_FILES) > [email protected] && \
> > +       mv [email protected] $@
> > diff --git a/lib/ovn-br-idl.ann b/lib/ovn-br-idl.ann
> > new file mode 100644
> > index 0000000000..80993338a6
> > --- /dev/null
> > +++ b/lib/ovn-br-idl.ann
> > @@ -0,0 +1,9 @@
> > +# -*- python -*-
> > +
> > +# This code, when invoked by "ovsdb-idlc annotate" (by the build
> > +# process), annotates ovn-br.ovsschema with additional data that give
> > +# the ovsdb-idl engine information about the types involved, so that
> > +# it can generate more programmer-friendly data structures.
> > +
> > +s["idlPrefix"] = "ovnbrrec_"
> > +s["idlHeader"] = "\"lib/ovn-br-idl.h\""
> > diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> > index d27983d1eb..2afa0972c7 100644
> > --- a/lib/ovn-util.c
> > +++ b/lib/ovn-util.c
> > @@ -566,6 +566,19 @@ default_ic_sb_db(void)
> >      return def;
> >  }
> >
> > +const char *
> > +default_br_db(void)
> > +{
> > +    static char *def;
> > +    if (!def) {
> > +        def = getenv("OVN_BR_DB");
> > +        if (!def) {
> > +            def = xasprintf("unix:%s/ovnbr_db.sock", ovn_rundir());
> > +        }
> > +    }
> > +    return def;
> > +}
> > +
> >  char *
> >  get_abs_unix_ctl_path(const char *path)
> >  {
> > @@ -1648,3 +1661,21 @@ normalize_addr_str(const char *orig_addr)
> >
> >      return ret;
> >  }
> > +
> > +bool
> > +is_partial_uuid_match(const struct uuid *uuid, const char *match)
> > +{
> > +    char uuid_s[UUID_LEN + 1];
> > +    snprintf(uuid_s, sizeof uuid_s, UUID_FMT, UUID_ARGS(uuid));
> > +
> > +    /* We strip leading zeros because we want to accept cookie values 
> > derived
> > +     * from UUIDs, and cookie values are printed without leading zeros 
> > because
> > +     * they're just numbers. */
> > +    const char *s1 = strip_leading_zero(uuid_s);
> > +    const char *s2 = match;
> > +    if (is_uuid_with_prefix(s2)) {
> > +        s2 = s2 + 2;
> > +    }
> > +    s2 = strip_leading_zero(s2);
> > +    return !strncmp(s1, s2, strlen(s2));
> > +}
> > diff --git a/lib/ovn-util.h b/lib/ovn-util.h
> > index 7a5bb9559e..9fc171b71c 100644
> > --- a/lib/ovn-util.h
> > +++ b/lib/ovn-util.h
> > @@ -134,6 +134,7 @@ const char *default_nb_db(void);
> >  const char *default_sb_db(void);
> >  const char *default_ic_nb_db(void);
> >  const char *default_ic_sb_db(void);
> > +const char *default_br_db(void);
> >  char *get_abs_unix_ctl_path(const char *path);
> >
> >  struct ovsdb_idl_table_class;
> > @@ -594,6 +595,21 @@ dynamic_bitmap_scan(struct dynamic_bitmap *dp, bool 
> > target, size_t start)
> >  #define DYNAMIC_BITMAP_FOR_EACH_1(IDX, MAP)   \
> >          BITMAP_FOR_EACH_1(IDX, (MAP)->capacity, (MAP)->map)
> >
> > +
> > +static inline const char *
> > +strip_leading_zero(const char *s)
> > +{
> > +    return s + strspn(s, "0");
> > +}
> > +
> > +static inline bool
> > +is_uuid_with_prefix(const char *uuid)
> > +{
> > +     return uuid[0] == '0' && (uuid[1] == 'x' || uuid[1] == 'X');
> > +}
> > +
> > +bool is_partial_uuid_match(const struct uuid *uuid, const char *match);
> > +
> >  /* Utilities around properly handling exit command. */
> >  struct ovn_exit_args {
> >      struct unixctl_conn **conns;
> > diff --git a/ovn-br.ovsschema b/ovn-br.ovsschema
> > new file mode 100644
> > index 0000000000..1fc66ec99e
> > --- /dev/null
> > +++ b/ovn-br.ovsschema
> > @@ -0,0 +1,94 @@
> > +{
> > +    "name": "OVN_Bridge_Controller",
> > +    "version": "0.0.1",
> > +    "cksum": "1701378742 4390",
> > +    "tables": {
> > +        "BR_Global": {
> > +            "columns": {
> > +                "br_cfg": {"type": {"key": "integer"}},
> > +                "external_ids": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}},
> > +                "connections": {
> > +                        "type": {"key": {"type": "uuid",
> > +                                        "refTable": "Connection"},
> > +                                        "min": 0,
> > +                                        "max": "unlimited"}},
> > +                "ssl": {
> > +                    "type": {"key": {"type": "uuid",
> > +                                    "refTable": "SSL"},
> > +                                    "min": 0, "max": 1}},
> > +                "options": {
> > +                    "type": {"key": "string", "value": "string",
> > +                            "min": 0, "max": "unlimited"}}},
> > +            "maxRows": 1,
> > +            "isRoot": true},
> > +        "Connection": {
> > +            "columns": {
> > +                "target": {"type": "string"},
> > +                "max_backoff": {"type": {"key": {"type": "integer",
> > +                                         "minInteger": 1000},
> > +                                         "min": 0,
> > +                                         "max": 1}},
> > +                "inactivity_probe": {"type": {"key": "integer",
> > +                                              "min": 0,
> > +                                              "max": 1}},
> > +                "other_config": {"type": {"key": "string",
> > +                                          "value": "string",
> > +                                          "min": 0,
> > +                                          "max": "unlimited"}},
> > +                "external_ids": {"type": {"key": "string",
> > +                                 "value": "string",
> > +                                 "min": 0,
> > +                                 "max": "unlimited"}},
> > +                "is_connected": {"type": "boolean", "ephemeral": true},
> > +                "status": {"type": {"key": "string",
> > +                                    "value": "string",
> > +                                    "min": 0,
> > +                                    "max": "unlimited"},
> > +                                    "ephemeral": true}},
> > +            "indexes": [["target"]]},
> > +        "SSL": {
> > +            "columns": {
> > +                "private_key": {"type": "string"},
> > +                "certificate": {"type": "string"},
> > +                "ca_cert": {"type": "string"},
> > +                "bootstrap_ca_cert": {"type": "boolean"},
> > +                "ssl_protocols": {"type": "string"},
> > +                "ssl_ciphers": {"type": "string"},
> > +                "ssl_ciphersuites": {"type": "string"},
> > +                "external_ids": {"type": {"key": "string",
> > +                                          "value": "string",
> > +                                          "min": 0,
> > +                                          "max": "unlimited"}}},
> > +            "maxRows": 1},
> > +        "Bridge": {
> > +            "columns": {
> > +                "name": {"type": "string"},
> > +                "options": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}},
> > +                "external_ids": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}}},
> > +            "indexes": [["name"]],
> > +            "isRoot": true},
> > +        "Logical_Flow": {
> > +            "columns": {
> > +                "bridge": {"type": {"key": {"type": "uuid",
> > +                                      "refTable": "Bridge"},
> > +                           "min": 0, "max": 1}},
> > +                "table_id": {"type": {"key": {"type": "integer",
> > +                                              "minInteger": 0,
> > +                                              "maxInteger": 100}}},
> > +                "priority": {"type": {"key": {"type": "integer",
> > +                                              "minInteger": 0,
> > +                                              "maxInteger": 65535}}},
> > +                "match": {"type": "string"},
> > +                "actions": {"type": "string"},
> > +                "external_ids": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}}},
> > +            "isRoot": true}
>
> Shall we define an index for the table:
> <bridge, table_id, priority, match> ??
>
Makes sense to me.  I'll address this in v4.

Thanks
Numan

> Best regards,
> Han
>
> > +    }
> > +}
> > diff --git a/ovn-br.xml b/ovn-br.xml
> > new file mode 100644
> > index 0000000000..be6232094b
> > --- /dev/null
> > +++ b/ovn-br.xml
> > @@ -0,0 +1,452 @@
> > +<?xml version="1.0" encoding="utf-8"?>
> > +<database name="ovn-bridge-controller" title="OVN Bridge Controller 
> > Database">
> > +  <p>
> > +    This database is the interface between OVN Bridge Controller and the
> > +    cloud management system (CMS) to program and control the OVS bridges
> > +    using OVN logical flows. The CMS produces almost all of
> > +    the contents of the database.  The <code>ovn-bridge-controller</code> 
> > program
> > +    monitors the database contents and programs the OVS bridges with the
> > +    OpenFlow rules.
> > +  </p>
> > +
> > +  <h2>External IDs</h2>
> > +
> > +  <p>
> > +    Each of the tables in this database contains a special column, named
> > +    <code>external_ids</code>.  This column has the same form and purpose 
> > each
> > +    place it appears.
> > +  </p>
> > +
> > +  <dl>
> > +    <dt><code>external_ids</code>: map of string-string pairs</dt>
> > +    <dd>
> > +      Key-value pairs for use by the CMS.  The CMS might use certain 
> > pairs, for
> > +      example, to identify entities in its own configuration that 
> > correspond to
> > +      those in this database.
> > +    </dd>
> > +  </dl>
> > +
> > +  <table name="BR_Global" title="Bridge Controller configuration">
> > +    <column name="br_cfg">
> > +      Sequence number for client to increment.  When a client modifies any
> > +      part of the bridge Controller database configuration and wishes to 
> > wait
> > +      for <code>ovn-br-controller</code> to finish applying the changes, 
> > it may
> > +      increment this sequence number.
> > +    </column>
> > +
> > +    <group title="Common Columns">
> > +        <column name="external_ids">
> > +        See <em>External IDs</em> at the beginning of this document.
> > +      </column>
> > +    </group>
> > +
> > +    <group title="Common options">
> > +      <column name="options">
> > +        This column provides general key/value settings. The supported
> > +        options are described individually below.
> > +      </column>
> > +    </group>
> > +
> > +    <group title="Connection Options">
> > +      <column name="connections">
> > +        Database clients to which the Open vSwitch database server should
> > +        connect or on which it should listen, along with options for how 
> > these
> > +        connections should be configured.  See the <ref 
> > table="Connection"/>
> > +        table for more information.
> > +      </column>
> > +      <column name="ssl">
> > +        Global SSL/TLS configuration.
> > +      </column>
> > +    </group>
> > +  </table>
> > +
> > +  <table name="Connection" title="OVSDB client connections.">
> > +    <p>
> > +      Configuration for a database connection to an Open vSwitch database
> > +      (OVSDB) client.
> > +    </p>
> > +
> > +    <p>
> > +      This table primarily configures the Open vSwitch database server
> > +      (<code>ovsdb-server</code>).
> > +    </p>
> > +
> > +    <p>
> > +      The Open vSwitch database server can initiate and maintain active
> > +      connections to remote clients.  It can also listen for database
> > +      connections.
> > +    </p>
> > +
> > +    <group title="Core Features">
> > +      <column name="target">
> > +        <p>Connection methods for clients.</p>
> > +        <p>
> > +          The following connection methods are currently supported:
> > +        </p>
> > +        <dl>
> > +          
> > <dt><code>ssl:<var>host</var></code>[<code>:<var>port</var></code>]</dt>
> > +          <dd>
> > +            <p>
> > +              The specified SSL/TLS <var>port</var> on the host at the 
> > given
> > +              <var>host</var>, which can either be a DNS name (if built 
> > with
> > +              unbound library) or an IP address. A valid SSL/TLS 
> > configuration
> > +              must be provided when this form is used, this configuration 
> > can
> > +              be specified via command-line options or the <ref 
> > table="SSL"/>
> > +              table.
> > +            </p>
> > +            <p>
> > +              If <var>port</var> is not specified, it defaults to 6640.
> > +            </p>
> > +            <p>
> > +              SSL/TLS support is an optional feature that is not always
> > +              built as part of OVN or Open vSwitch.
> > +            </p>
> > +          </dd>
> > +
> > +          
> > <dt><code>tcp:<var>host</var></code>[<code>:<var>port</var></code>]</dt>
> > +          <dd>
> > +            <p>
> > +              The specified TCP <var>port</var> on the host at the given
> > +              <var>host</var>, which can either be a DNS name (if built 
> > with
> > +              unbound library) or an IP address.  If <var>host</var> is an 
> > IPv6
> > +              address, wrap it in square brackets, e.g. 
> > <code>tcp:[::1]:6640</code>.
> > +            </p>
> > +            <p>
> > +              If <var>port</var> is not specified, it defaults to 6640.
> > +            </p>
> > +          </dd>
> > +          
> > <dt><code>pssl:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt>
> > +          <dd>
> > +            <p>
> > +              Listens for SSL/TLS connections on the specified TCP
> > +              <var>port</var>.
> > +              Specify 0 for <var>port</var> to have the kernel 
> > automatically
> > +              choose an available port.  If <var>host</var>, which can 
> > either
> > +              be a DNS name (if built with unbound library) or an IP 
> > address,
> > +              is specified, then connections are restricted to the 
> > resolved or
> > +              specified local IPaddress (either IPv4 or IPv6 address).  If
> > +              <var>host</var> is an IPv6 address, wrap in square brackets,
> > +              e.g. <code>pssl:6640:[::1]</code>.  If <var>host</var> is not
> > +              specified then it listens only on IPv4 (but not IPv6) 
> > addresses.
> > +              A valid SSL/TLS configuration must be provided when this 
> > form is
> > +              used, this can be specified either via command-line options 
> > or
> > +              the <ref table="SSL"/> table.
> > +            </p>
> > +            <p>
> > +              If <var>port</var> is not specified, it defaults to 6640.
> > +            </p>
> > +            <p>
> > +              SSL/TLS support is an optional feature that is not always 
> > built
> > +              as part of OVN or Open vSwitch.
> > +            </p>
> > +          </dd>
> > +          
> > <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt>
> > +          <dd>
> > +            <p>
> > +              Listens for connections on the specified TCP <var>port</var>.
> > +              Specify 0 for <var>port</var> to have the kernel 
> > automatically
> > +              choose an available port.  If <var>host</var>, which can 
> > either
> > +              be a DNS name (if built with unbound library) or an IP 
> > address,
> > +              is specified, then connections are restricted to the 
> > resolved or
> > +              specified local IP address (either IPv4 or IPv6 address).  If
> > +              <var>host</var> is an IPv6 address, wrap it in square 
> > brackets,
> > +              e.g. <code>ptcp:6640:[::1]</code>.  If <var>host</var> is not
> > +              specified then it listens only on IPv4 addresses.
> > +            </p>
> > +            <p>
> > +              If <var>port</var> is not specified, it defaults to 6640.
> > +            </p>
> > +          </dd>
> > +        </dl>
> > +        <p>When multiple clients are configured, the <ref column="target"/>
> > +        values must be unique.  Duplicate <ref column="target"/> values 
> > yield
> > +        unspecified results.</p>
> > +      </column>
> > +    </group>
> > +
> > +    <group title="Client Failure Detection and Handling">
> > +      <column name="max_backoff">
> > +        Maximum number of milliseconds to wait between connection attempts.
> > +        Default is implementation-specific.
> > +      </column>
> > +
> > +      <column name="inactivity_probe">
> > +        Maximum number of milliseconds of idle time on connection to the 
> > client
> > +        before sending an inactivity probe message.  If Open vSwitch does 
> > not
> > +        communicate with the client for the specified number of seconds, it
> > +        will send a probe.  If a response is not received for the same
> > +        additional amount of time, Open vSwitch assumes the connection has 
> > been
> > +        broken and attempts to reconnect.  Default is 
> > implementation-specific.
> > +        A value of 0 disables inactivity probes.
> > +      </column>
> > +    </group>
> > +
> > +    <group title="Status">
> > +      <p>
> > +        Key-value pair of <ref column="is_connected"/> is always updated.
> > +        Other key-value pairs in the status columns may be updated depends
> > +        on the <ref column="target"/> type.
> > +      </p>
> > +
> > +      <p>
> > +        When <ref column="target"/> specifies a connection method that
> > +        listens for inbound connections (e.g. <code>ptcp:</code> or
> > +        <code>punix:</code>), both <ref column="n_connections"/> and
> > +        <ref column="is_connected"/> may also be updated while the
> > +        remaining key-value pairs are omitted.
> > +      </p>
> > +
> > +      <p>
> > +        On the other hand, when <ref column="target"/> specifies an
> > +        outbound connection, all key-value pairs may be updated, except
> > +        the above-mentioned two key-value pairs associated with inbound
> > +        connection targets. They are omitted.
> > +      </p>
> > +
> > +      <column name="is_connected">
> > +        <code>true</code> if currently connected to this client,
> > +        <code>false</code> otherwise.
> > +      </column>
> > +
> > +      <column name="status" key="last_error">
> > +        A human-readable description of the last error on the connection
> > +        to the manager; i.e. <code>strerror(errno)</code>.  This key
> > +        will exist only if an error has occurred.
> > +      </column>
> > +
> > +      <column name="status" key="state"
> > +              type='{"type": "string", "enum": ["set", ["VOID", "BACKOFF", 
> > "CONNECTING", "ACTIVE", "IDLE"]]}'>
> > +        <p>
> > +          The state of the connection to the manager:
> > +        </p>
> > +        <dl>
> > +          <dt><code>VOID</code></dt>
> > +          <dd>Connection is disabled.</dd>
> > +
> > +          <dt><code>BACKOFF</code></dt>
> > +          <dd>Attempting to reconnect at an increasing period.</dd>
> > +
> > +          <dt><code>CONNECTING</code></dt>
> > +          <dd>Attempting to connect.</dd>
> > +
> > +          <dt><code>ACTIVE</code></dt>
> > +          <dd>Connected, remote host responsive.</dd>
> > +
> > +          <dt><code>IDLE</code></dt>
> > +          <dd>Connection is idle.  Waiting for response to keep-alive.</dd>
> > +        </dl>
> > +        <p>
> > +          These values may change in the future.  They are provided only 
> > for
> > +          human consumption.
> > +        </p>
> > +      </column>
> > +
> > +      <column name="status" key="sec_since_connect"
> > +              type='{"type": "integer", "minInteger": 0}'>
> > +        The amount of time since this client last successfully connected
> > +        to the database (in seconds). Value is empty if client has never
> > +        successfully been connected.
> > +      </column>
> > +
> > +      <column name="status" key="sec_since_disconnect"
> > +              type='{"type": "integer", "minInteger": 0}'>
> > +        The amount of time since this client last disconnected from the
> > +        database (in seconds). Value is empty if client has never
> > +        disconnected.
> > +      </column>
> > +
> > +      <column name="status" key="locks_held">
> > +        Space-separated list of the names of OVSDB locks that the 
> > connection
> > +        holds.  Omitted if the connection does not hold any locks.
> > +      </column>
> > +
> > +      <column name="status" key="locks_waiting">
> > +        Space-separated list of the names of OVSDB locks that the 
> > connection is
> > +        currently waiting to acquire.  Omitted if the connection is not 
> > waiting
> > +        for any locks.
> > +      </column>
> > +
> > +      <column name="status" key="locks_lost">
> > +        Space-separated list of the names of OVSDB locks that the 
> > connection
> > +        has had stolen by another OVSDB client.  Omitted if no locks have 
> > been
> > +        stolen from this connection.
> > +      </column>
> > +
> > +      <column name="status" key="n_connections"
> > +              type='{"type": "integer", "minInteger": 2}'>
> > +        When <ref column="target"/> specifies a connection method that
> > +        listens for inbound connections (e.g. <code>ptcp:</code> or
> > +        <code>pssl:</code>) and more than one connection is actually 
> > active,
> > +        the value is the number of active connections.  Otherwise, this
> > +        key-value pair is omitted.
> > +      </column>
> > +
> > +      <column name="status" key="bound_port" type='{"type": "integer"}'>
> > +        When <ref column="target"/> is <code>ptcp:</code> or
> > +        <code>pssl:</code>, this is the TCP port on which the OVSDB server 
> > is
> > +        listening.  (This is particularly useful when <ref
> > +        column="target"/> specifies a port of 0, allowing the kernel to
> > +        choose any available port.)
> > +      </column>
> > +    </group>
> > +
> > +    <group title="Common Columns">
> > +      The overall purpose of these columns is described under <code>Common
> > +      Columns</code> at the beginning of this document.
> > +
> > +      <column name="external_ids"/>
> > +      <column name="other_config"/>
> > +    </group>
> > +  </table>
> > +
> > +  <table name="SSL">
> > +    SSL/TLS configuration for ovn-nb database access.
> > +
> > +    <column name="private_key">
> > +      Name of a PEM file containing the private key used as the switch's
> > +      identity for SSL/TLS connections to the controller.
> > +    </column>
> > +
> > +    <column name="certificate">
> > +      Name of a PEM file containing a certificate, signed by the
> > +      certificate authority (CA) used by the controller and manager,
> > +      that certifies the switch's private key, identifying a trustworthy
> > +      switch.
> > +    </column>
> > +
> > +    <column name="ca_cert">
> > +      Name of a PEM file containing the CA certificate used to verify
> > +      that the switch is connected to a trustworthy controller.
> > +    </column>
> > +
> > +    <column name="bootstrap_ca_cert">
> > +      If set to <code>true</code>, then Open vSwitch will attempt to
> > +      obtain the CA certificate from the controller on its first SSL/TLS
> > +      connection and save it to the named PEM file. If it is successful,
> > +      it will immediately drop the connection and reconnect, and from then
> > +      on all SSL/TLS connections must be authenticated by a certificate 
> > signed
> > +      by the CA certificate thus obtained.  <em>This option exposes the
> > +      SSL/TLS connection to a man-in-the-middle attack obtaining the 
> > initial
> > +      CA certificate.</em>  It may still be useful for bootstrapping.
> > +    </column>
> > +
> > +    <column name="ssl_protocols">
> > +      <p>
> > +        Range or a comma- or space-delimited list of the SSL/TLS protocols 
> > to
> > +        enable for SSL/TLS connections.
> > +      </p>
> > +      <p>
> > +        Supported protocols include <code>TLSv1.2</code> and
> > +        <code>TLSv1.3</code>.  Ranges can be provided in a form of two 
> > protocol
> > +        names separated with a dash (<code>TLSv1.2-TLSv1.3</code>), or as a
> > +        single protocol name with a plus sign (<code>TLSv1.2+</code>).  The
> > +        value can be a list of protocols or exactly one range.  The range 
> > is a
> > +        preferred way of specifying protocols and the configuration always
> > +        behaves as if the range between the minimum and the maximum 
> > specified
> > +        version is provided, i.e., if the value is set to
> > +        <code>TLSv1.X,TLSv1.(X+2)</code>, the <code>TLSv1.(X+1)</code> will
> > +        also be enabled as if it was a range.
> > +        Regardless of order, the highest protocol supported by both sides 
> > will
> > +        be chosen when making the connection.
> > +      </p>
> > +      <p>
> > +        The default when this option is omitted is <code>TLSv1.2+</code>.
> > +      </p>
> > +    </column>
> > +
> > +    <column name="ssl_ciphers">
> > +      List of ciphers (in OpenSSL cipher string format) to be supported
> > +      for SSL/TLS connections with TLSv1.2.  The default when this option
> > +      is omitted is <code>DEFAULT:@SECLEVEL=2</code>.
> > +    </column>
> > +
> > +    <column name="ssl_ciphersuites">
> > +      List of ciphersuites (in OpenSSL ciphersuites string format) to be
> > +      supported for SSL/TLS connections with TLSv1.3 and later.  Default 
> > value
> > +      from OpenSSL will be used when this option is omitted.
> > +    </column>
> > +
> > +    <group title="Common Columns">
> > +      The overall purpose of these columns is described under <code>Common
> > +      Columns</code> at the beginning of this document.
> > +
> > +      <column name="external_ids"/>
> > +    </group>
> > +  </table>
> > +
> > +  <table name="Bridge" title="OVS Bridge to control">
> > +    <column name="name">
> > +      Name of the OVS bridge.  This bridge should exist in the local OVS
> > +      database.
> > +    </column>
> > +
> > +    <column name="options">
> > +      Reserved for future use.
> > +    </column>
> > +
> > +    <column name="external_ids">
> > +      See <em>External IDs</em> at the beginning of this document.
> > +    </column>
> > +  </table>
> > +
> > +  <table name="Logical_Flow" title="Logical flow">
> > +    <column name="bridge">
> > +      The bridge to which the logical flow belongs to.
> > +    </column>
> > +
> > +    <column name="table_id">
> > +      The stage in the logical pipeline, analogous to an OpenFlow table 
> > number.
> > +    </column>
> > +
> > +    <column name="priority">
> > +      The flow's priority.  Flows with numerically higher priority take
> > +      precedence over those with lower.  If two logical flows with the
> > +      same priority both match, then the one actually applied to the 
> > packet is
> > +      undefined.
> > +    </column>
> > +
> > +    <column name="match">
> > +      <p>
> > +        A matching expression.  OVN provides a superset of OpenFlow 
> > matching
> > +        capabilities, using a syntax similar to Boolean expressions in a
> > +        programming language.
> > +      </p>
> > +
> > +      <p>
> > +        Please see the documentation of the
> > +        <ref
> > +        column="match" table="Logical_Flow" db="OVN_Southbound"/>
> > +        column of the <ref table="Logical_Flow"
> > +        db="OVN_Southbound"/> table in the <ref db="OVN_Southbound"/> 
> > database.
> > +      </p>
> > +    </column>
> > +
> > +    <column name="actions">
> > +      <p>
> > +        Logical datapath actions, to be executed when the logical flow
> > +        represented by this row is the highest-priority match.
> > +      </p>
> > +
> > +      <p>
> > +        Actions share lexical syntax with the <ref column="match"/> 
> > column.  An
> > +        empty set of actions (or one that contains just white space or
> > +        comments), or a set of actions that consists of just
> > +        <code>drop;</code>, causes the matched packets to be dropped.
> > +        Otherwise, the column should contain a sequence of actions, each
> > +        terminated by a semicolon.
> > +      </p>
> > +
> > +      <p>
> > +        Please see the documentation of the
> > +        <ref
> > +        column="actions" table="Logical_Flow" db="OVN_Southbound"/>
> > +        column of the <ref table="Logical_Flow"
> > +        db="OVN_Southbound"/> table in the <ref db="OVN_Southbound"/> 
> > database.
> > +      </p>
> > +    </column>
> > +
> > +    <column name="external_ids">
> > +      See <em>External IDs</em> at the beginning of this document.
> > +    </column>
> > +  </table>
> > +</database>
> > diff --git a/rhel/automake.mk b/rhel/automake.mk
> > index 445dcd2fd4..c4ebf89d8a 100644
> > --- a/rhel/automake.mk
> > +++ b/rhel/automake.mk
> > @@ -18,7 +18,9 @@ EXTRA_DIST += \
> >         rhel/usr_lib_systemd_system_ovn-northd.service \
> >         rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
> >         rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
> > -       rhel/usr_share_ovn_scripts_systemd_sysconfig.template
> > +       rhel/usr_share_ovn_scripts_systemd_sysconfig.template \
> > +       rhel/usr_lib_systemd_system_ovn-br-controller.service \
> > +       rhel/usr_lib_systemd_system_ovn-br-db.service
> >
> >  update_rhel_spec = \
> >    $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \
> > diff --git a/rhel/ovn-fedora.spec.in b/rhel/ovn-fedora.spec.in
> > index 670f1ca9eb..a1ab46ebef 100644
> > --- a/rhel/ovn-fedora.spec.in
> > +++ b/rhel/ovn-fedora.spec.in
> > @@ -119,6 +119,14 @@ Provides: openvswitch-ovn-docker = 
> > %{?epoch:%{epoch}:}%{version}-%{release}
> >  %description docker
> >  Docker network plugins for OVN.
> >
> > +%package br-controller
> > +Summary: Open Virtual Network support
> > +License: ASL 2.0
> > +Requires: ovn
> > +
> > +%description br-controller
> > +OVN bridge controller
> > +
> >  %prep
> >  %autosetup -n ovn-%{version} -a 10 -p 1
> >
> > @@ -165,7 +173,7 @@ install -p -D -m 0644 \
> >          rhel/usr_share_ovn_scripts_systemd_sysconfig.template \
> >          $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/ovn
> >
> > -for service in ovn-controller ovn-controller-vtep ovn-northd ovn-ic 
> > ovn-ic-db ovn-db@; do
> > +for service in ovn-controller ovn-controller-vtep ovn-northd ovn-ic 
> > ovn-ic-db ovn-db@ ovn-br-controller ovn-br-db; do
> >          install -p -D -m 0644 \
> >                          rhel/usr_lib_systemd_system_${service}.service \
> >                          $RPM_BUILD_ROOT%{_unitdir}/${service}.service
> > @@ -549,7 +557,19 @@ fi
> >  %{_mandir}/man8/ovn-controller-vtep.8*
> >  %{_unitdir}/ovn-controller-vtep.service
> >
> > +%files br-controller
> > +%{_bindir}/ovn-br-controller
> > +%{_bindir}/ovn-brctl
> > +%{_mandir}/man8/ovn-br-controller.8*
> > +%{_mandir}/man5/ovn-br.5*
> > +%config %{_datadir}/ovn/ovn-br.ovsschema
> > +%{_unitdir}/ovn-br-controller.service
> > +%{_unitdir}/ovn-br-db.service
> > +
> >  %changelog
> > +* Wed Sep 24 2025 Numan Siddique <[email protected]>
> > +- Added ovn-br-controller systemd-units.
> > +
> >  * Wed Jan 11 2023 Vladislav Odintsov <[email protected]>
> >  - Added [email protected] systemd-unit.
> >
> > diff --git a/rhel/usr_lib_systemd_system_ovn-br-controller.service 
> > b/rhel/usr_lib_systemd_system_ovn-br-controller.service
> > new file mode 100644
> > index 0000000000..9f1e3f4b81
> > --- /dev/null
> > +++ b/rhel/usr_lib_systemd_system_ovn-br-controller.service
> > @@ -0,0 +1,35 @@
> > +# See ovn-br-controller(8) for details about ovn-br-controller.
> > +#
> > +# To customize the ovn-br-controller service, you may create a 
> > configuration file
> > +# in the /etc/systemd/system/ovn-br-controller.d/ directory.  For example, 
> > to specify
> > +# additional options to be passed to the "ovn-ctl start_ovnbr_controller" 
> > command, you
> > +# could place the following contents in
> > +# /etc/systemd/system/ovn-br-controller.d/local.conf:
> > +#
> > +#   [System]
> > +#   
> > Environment="OVNBR_CONTROLLER_OPTS=--ovnbr-controller-log=-vconsole:emer 
> > -vsyslog:err -vfile:info"
> > +#
> > +# Alternatively, you may specify environment variables in the file 
> > /etc/sysconfig/ovn-controller:
> > +#
> > +#   OVNBR_CONTROLLER_OPTS="--ovnbr-controller-log=-vconsole:emer 
> > -vsyslog:err -vfile:info"
> > +
> > +[Unit]
> > +Description=OVN Bridge controller daemon
> > +After=syslog.target
> > +Requires=openvswitch.service
> > +After=openvswitch.service
> > +
> > +[Service]
> > +Type=forking
> > +PIDFile=/var/run/ovn/ovn-br-controller.pid
> > +Restart=on-failure
> > +Environment=OVN_RUNDIR=%t/ovn OVS_RUNDIR=%t/openvswitch
> > +EnvironmentFile=-/etc/sysconfig/ovn
> > +EnvironmentFile=-/etc/sysconfig/ovn-br-controller
> > +ExecStart=/usr/share/ovn/scripts/ovn-ctl --no-monitor \
> > +           --ovn-user=${OVN_USER_ID} \
> > +          start_ovnbr_controller $OVNBR_CONTROLLER_OPTS
> > +ExecStop=/usr/share/ovn/scripts/ovn-ctl stop_ovnbr_controller
> > +
> > +[Install]
> > +WantedBy=multi-user.target
> > diff --git a/rhel/usr_lib_systemd_system_ovn-br-db.service 
> > b/rhel/usr_lib_systemd_system_ovn-br-db.service
> > new file mode 100644
> > index 0000000000..6de2a22f1e
> > --- /dev/null
> > +++ b/rhel/usr_lib_systemd_system_ovn-br-db.service
> > @@ -0,0 +1,32 @@
> > +# See ovn-br(8) for details about ovn-br.
> > +#
> > +# To customize the ovn-br-db service, you may create a configuration file
> > +# in the /etc/systemd/system/ovn-br-db.d/ directory.  For example, to 
> > specify
> > +# additional options to be passed to the "ovn-ctl start_ovnbr_ovsdb" 
> > command, you
> > +# could place the following contents in
> > +# /etc/systemd/system/ovn-br-db.d/local.conf:
> > +#
> > +#   [System]
> > +#   Environment="OVN_BR_DB_OPTS=--db-br-create-insecure-remote=yes"
> > +#
> > +# Alternatively, you may specify environment variables in the file 
> > /etc/sysconfig/ovn-br-db:
> > +#
> > +#   OVN_BR_DB_OPTS="--db-br-create-insecure-remote=yes"
> > +
> > +[Unit]
> > +Description=OVN Bridge Controller OVSDB server
> > +After=syslog.target
> > +
> > +[Service]
> > +Type=oneshot
> > +RemainAfterExit=yes
> > +Environment=OVN_RUNDIR=%t/ovn OVN_DBDIR=/var/lib/ovn
> > +EnvironmentFile=-/etc/sysconfig/ovn
> > +EnvironmentFile=-/etc/sysconfig/ovn-br-db
> > +ExecStartPre=-/usr/bin/chown -R ${OVN_USER_ID} ${OVN_DBDIR}
> > +ExecStart=/usr/share/ovn/scripts/ovn-ctl \
> > +          --ovn-user=${OVN_USER_ID} start_ovnbr_ovsdb $OVN_BR_DB_OPTS
> > +ExecStop=/usr/share/ovn/scripts/ovn-ctl stop_ovnbr_ovsdb
> > +
> > +[Install]
> > +WantedBy=multi-user.target
> > diff --git a/tutorial/ovn-sandbox b/tutorial/ovn-sandbox
> > index a689eb30f9..970afde5cd 100755
> > --- a/tutorial/ovn-sandbox
> > +++ b/tutorial/ovn-sandbox
> > @@ -64,6 +64,8 @@ gdb_ovn_controller=false
> >  gdb_ovn_controller_ex=false
> >  gdb_ovn_controller_vtep=false
> >  gdb_ovn_controller_vtep_ex=false
> > +gdb_ovn_br_controller=false
> > +gdb_ovn_br_controller_ex=false
> >  builddir=
> >  ovsbuilddir=
> >  srcdir=
> > @@ -90,6 +92,7 @@ ic_nb_servers=3
> >  ic_sb_model=clustered
> >  ic_sb_servers=3
> >  dummy=override
> > +ovn_br=false
> >
> >  for option; do
> >      # This option-parsing mechanism borrowed from a Autoconf-generated
> > @@ -339,6 +342,9 @@ EOF
> >          --ic-sb-m*)
> >              prev=ic_sb_model
> >              ;;
> > +        --ovn-br)
> > +            ovn_br=true
> > +            ;;
> >          -R|--gdb-run)
> >              gdb_vswitchd_ex=true
> >              gdb_ovsdb_ex=true
> > @@ -422,6 +428,11 @@ if $built; then
> >          echo >&2 'source directory not found, please use --srcdir'
> >          exit 1
> >      fi
> > +    br_schema=$srcdir/ovn-br.ovsschema
> > +    if test ! -e "$br_schema"; then
> > +        echo >&2 'source directory not found, please use --srcdir'
> > +        exit 1
> > +    fi
> >
> >      # Put built tools early in $PATH.
> >      if test ! -e $ovsbuilddir/vswitchd/ovs-vswitchd; then
> > @@ -429,7 +440,7 @@ if $built; then
> >          exit 1
> >      fi
> >      
> > PATH=$ovsbuilddir/ovsdb:$ovsbuilddir/vswitchd:$ovsbuilddir/utilities:$ovsbuilddir/vtep:$PATH
> > -    
> > PATH=$builddir/controller:$builddir/controller-vtep:$builddir/northd:$builddir/ic:$builddir/utilities:$PATH
> > +    
> > PATH=$builddir/controller:$builddir/controller-vtep:$builddir/northd:$builddir/ic:$builddir/utilities:$builddir/br-controller:$PATH
> >      export PATH
> >  else
> >      case $schema in
> > @@ -616,6 +627,10 @@ ovn_start_db sb "$sbdb_model" "$sbdb_servers" 
> > "$ovnsb_source"
> >  ovn_start_db ic_nb "$ic_nb_model" "$ic_nb_servers" "$ic_nb_schema"
> >  ovn_start_db ic_sb "$ic_sb_model" "$ic_sb_servers" "$ic_sb_schema"
> >
> > +if $ovn_br; then
> > +    ovn_start_db br standalone 1 "$br_schema"
> > +fi
> > +
> >  #Add a small delay to allow ovsdb-server to launch.
> >  sleep 0.1
> >
> > @@ -692,6 +707,13 @@ rungdb $gdb_ovn_controller_vtep 
> > $gdb_ovn_controller_vtep_ex \
> >      $OVN_CTRLR_PKI --log-file -vsyslog:off \
> >      --ovnsb-db="$OVN_SB_DB"
> >
> > +if $ovn_br; then
> > +    run ovs-vsctl set open . external-ids:ovn-br-remote=$OVN_BR_DB
> > +    rungdb $gdb_ovn_br_controller $gdb_ovn_br_controller_ex 
> > ovn-br-controller \
> > +        --detach --no-chdir -vsyslog:off \
> > +        --log-file=ovn-br-controller.log \
> > +        --pidfile=ovn-br-controller.pid -vconsole:off
> > +fi
> >  cat <<EOF
> >
> >
> > diff --git a/utilities/automake.mk b/utilities/automake.mk
> > index 79cf780ab9..b620038d01 100644
> > --- a/utilities/automake.mk
> > +++ b/utilities/automake.mk
> > @@ -118,4 +118,12 @@ bin_PROGRAMS += utilities/ovn-debug
> >  utilities_ovn_debug_SOURCES = utilities/ovn-debug.c
> >  utilities_ovn_debug_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la 
> > $(OVS_LIBDIR)/libopenvswitch.la
> >
> > +# ovn-brctl
> > +bin_PROGRAMS += utilities/ovn-brctl
> > +utilities_ovn_brctl_SOURCES = \
> > +    utilities/ovn-dbctl.c \
> > +    utilities/ovn-dbctl.h \
> > +    utilities/ovn-brctl.c
> > +utilities_ovn_brctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la 
> > $(OVS_LIBDIR)/libopenvswitch.la
> > +
> >  include utilities/bugtool/automake.mk
> > diff --git a/utilities/ovn-brctl.c b/utilities/ovn-brctl.c
> > new file mode 100644
> > index 0000000000..60b62420a7
> > --- /dev/null
> > +++ b/utilities/ovn-brctl.c
> > @@ -0,0 +1,524 @@
> > +/*
> > + * 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 <inttypes.h>
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +/* OVS includes. */
> > +#include "command-line.h"
> > +#include "daemon.h"
> > +#include "db-ctl-base.h"
> > +#include "dirs.h"
> > +#include "openvswitch/vlog.h"
> > +
> > +/* OVN includes. */
> > +#include "lib/ovn-br-idl.h"
> > +#include "lib/ovn-dirs.h"
> > +#include "lib/ovn-util.h"
> > +#include "lib/vec.h"
> > +#include "ovn-dbctl.h"
> > +
> > +VLOG_DEFINE_THIS_MODULE(brctl);
> > +
> > +static char * OVS_WARN_UNUSED_RESULT br_by_name_or_uuid(
> > +    struct ctl_context *ctx, const char *id, bool must_exist,
> > +    const struct ovnbrrec_bridge **br_p);
> > +
> > +static void
> > +brctl_add_base_prerequisites(struct ovsdb_idl *idl,
> > +                             enum nbctl_wait_type wait_type OVS_UNUSED)
> > +{
> > +    ovsdb_idl_add_table(idl, &ovnbrrec_table_br_global);
> > +}
> > +
> > +static void
> > +brctl_pre_execute(struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn,
> > +                  enum nbctl_wait_type wait_type OVS_UNUSED)
> > +{
> > +    const struct ovnbrrec_br_global *br = ovnbrrec_br_global_first(idl);
> > +    if (!br) {
> > +        br = ovnbrrec_br_global_insert(txn);
> > +    }
> > +}
> > +
> > +static int
> > +get_inactivity_probe(struct ovsdb_idl *idl)
> > +{
> > +    const struct ovnbrrec_br_global *pr = ovnbrrec_br_global_first(idl);
> > +    int interval = DEFAULT_UTILS_PROBE_INTERVAL_MSEC;
> > +
> > +    if (pr) {
> > +        interval = smap_get_int(&pr->options, "brctl_probe_interval",
> > +                                interval);
> > +    }
> > +
> > +    return interval;
> > +}
> > +
> > +/* ovn-brctl specific context.  Inherits the 'struct ctl_context' as base. 
> > */
> > +struct brctl_context {
> > +    struct ctl_context base;
> > +};
> > +
> > +static struct ctl_context *
> > +brctl_ctx_create(void)
> > +{
> > +    struct brctl_context *prctx = xmalloc(sizeof *prctx);
> > +
> > +    return &prctx->base;
> > +}
> > +
> > +static void
> > +brctl_ctx_destroy(struct ctl_context *ctx)
> > +{
> > +    free(ctx);
> > +}
> > +
> > +static void
> > +print_br(const struct ovnbrrec_bridge *br, struct ds *s)
> > +{
> > +    ds_put_format(s, "bridge "UUID_FMT" (%s)\n",
> > +                  UUID_ARGS(&br->header_.uuid), br->name);
> > +}
> > +
> > +static void
> > +brctl_init(struct ctl_context *ctx OVS_UNUSED)
> > +{
> > +}
> > +
> > +static void
> > +brctl_pre_show(struct ctl_context *ctx)
> > +{
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_name);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_options);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_external_ids);
> > +}
> > +
> > +static const struct ctl_table_class tables[OVNBRREC_N_TABLES] = {
> > +    [OVNBRREC_TABLE_BRIDGE].row_ids[0]
> > +    = {&ovnbrrec_bridge_col_name, NULL, NULL},
> > +
> > +    [OVNBRREC_TABLE_CONNECTION].row_ids[0]
> > +    = {&ovnbrrec_connection_col_target, NULL, NULL},
> > +};
> > +
> > +static void
> > +brctl_show(struct ctl_context *ctx)
> > +{
> > +    const struct ovnbrrec_bridge *br;
> > +
> > +    if (ctx->argc == 2) {
> > +        char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &br);
> > +        if (error) {
> > +            ctx->error = error;
> > +            return;
> > +        }
> > +
> > +        print_br(br, &ctx->output);
> > +    } else {
> > +        OVNBRREC_BRIDGE_FOR_EACH (br, ctx->idl) {
> > +            print_br(br, &ctx->output);
> > +        }
> > +    }
> > +}
> > +
> > +static void
> > +pre_get_info(struct ctl_context *ctx)
> > +{
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_name);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_options);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_bridge_col_external_ids);
> > +
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_actions);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_bridge);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_match);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_priority);
> > +    ovsdb_idl_add_column(ctx->idl, &ovnbrrec_logical_flow_col_table_id);
> > +    ovsdb_idl_add_column(ctx->idl, 
> > &ovnbrrec_logical_flow_col_external_ids);
> > +}
> > +
> > +struct brctl_lflow {
> > +    const struct ovnbrrec_logical_flow *lflow;
> > +    const struct ovnbrrec_bridge *br;
> > +};
> > +
> > +static int
> > +brctl_lflow_cmp(const void *a_, const void *b_)
> > +{
> > +    const struct brctl_lflow *a_ctl_lflow = a_;
> > +    const struct brctl_lflow *b_ctl_lflow = b_;
> > +
> > +    const struct ovnbrrec_logical_flow *a = a_ctl_lflow->lflow;
> > +    const struct ovnbrrec_logical_flow *b = b_ctl_lflow->lflow;
> > +
> > +    const struct ovnbrrec_bridge *abr = a_ctl_lflow->br;
> > +    const struct ovnbrrec_bridge *bbr = b_ctl_lflow->br;
> > +    const char *a_name = abr->name;
> > +    const char *b_name = bbr->name;
> > +    int cmp = strcmp(a_name, b_name);
> > +    if (cmp) {
> > +        return cmp;
> > +    }
> > +
> > +    cmp = uuid_compare_3way(&abr->header_.uuid, &bbr->header_.uuid);
> > +    if (cmp) {
> > +        return cmp;
> > +    }
> > +
> > +    cmp = (a->table_id > b->table_id ? 1
> > +           : a->table_id < b->table_id ? -1
> > +           : a->priority > b->priority ? -1
> > +           : a->priority < b->priority ? 1
> > +           : strcmp(a->match, b->match));
> > +    return cmp ? cmp : strcmp(a->actions, b->actions);
> > +}
> > +
> > +static void
> > +brctl_lflow_add(struct vector *lflows,
> > +                const struct ovnbrrec_logical_flow *lflow,
> > +                const struct ovnbrrec_bridge *br)
> > +{
> > +    struct brctl_lflow brctl_lflow = (struct brctl_lflow) {
> > +        .lflow = lflow,
> > +        .br = br,
> > +    };
> > +    vector_push(lflows, &brctl_lflow);
> > +}
> > +
> > +static void
> > +print_uuid_part(const struct uuid *uuid, bool do_print, struct ds *s)
> > +{
> > +    if (!do_print) {
> > +        return;
> > +    }
> > +    ds_put_format(s, "uuid=0x%08"PRIx32", ", uuid->parts[0]);
> > +}
> > +
> > +static void
> > +print_bridge_prompt(const struct ovnbrrec_bridge *br,
> > +                    const struct uuid *uuid, struct ds *s) {
> > +        ds_put_format(s, "Bridge: %s", br->name);
> > +        ds_put_format(s, " ("UUID_FMT")\n",
> > +                      UUID_ARGS(uuid));
> > +}
> > +
> > +static char * OVS_WARN_UNUSED_RESULT
> > +parse_priority(const char *arg, int64_t *priority_p)
> > +{
> > +    /* Validate priority. */
> > +    int64_t priority;
> > +    if (!ovs_scan(arg, "%"SCNd64, &priority)
> > +        || priority < 0 || priority > 32767) {
> > +        /* Priority_p could be uninitialized as no valid priority was
> > +         * input, initialize it to a valid value of 0 before returning */
> > +        *priority_p = 0;
> > +        return xasprintf("%s: priority must in range 0...32767", arg);
> > +    }
> > +    *priority_p = priority;
> > +    return NULL;
> > +}
> > +
> > +static char * OVS_WARN_UNUSED_RESULT
> > +parse_table_id(const char *arg, int64_t *table_p)
> > +{
> > +    /* Validate table id. */
> > +    int64_t table;
> > +    if (!ovs_scan(arg, "%"SCNd64, &table)
> > +        || table < 0 || table > 55) {
> > +        /* table_p could be uninitialized as no valid table id was
> > +         * input, initialize it to a valid value of 0 before returning */
> > +        *table_p = 0;
> > +        return xasprintf("%s: table must in range 0...55", arg);
> > +    }
> > +    *table_p = table;
> > +    return NULL;
> > +}
> > +
> > +static char * OVS_WARN_UNUSED_RESULT
> > +br_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool 
> > must_exist,
> > +                   const struct ovnbrrec_bridge **br_p)
> > +{
> > +    const struct ovsdb_idl_row *row = NULL;
> > +
> > +    char *error = ctl_get_row(ctx, &ovnbrrec_table_bridge,
> > +                              id, must_exist, &row);
> > +    *br_p = (const struct ovnbrrec_bridge *) row;
> > +    return error;
> > +}
> > +
> > +static void
> > +cmd_br_add(struct ctl_context *ctx)
> > +{
> > +    const struct ovnbrrec_bridge *bridge = NULL;
> > +
> > +    char *error = br_by_name_or_uuid(ctx, ctx->argv[1], false, &bridge);
> > +    if (error) {
> > +        ctl_error(ctx, "%s", error);
> > +        free(error);
> > +        return;
> > +    }
> > +
> > +    if (bridge) {
> > +        ctl_error(ctx, "Bridge %s already exists", ctx->argv[1]);
> > +    }
> > +
> > +    bridge = ovnbrrec_bridge_insert(ctx->txn);
> > +    ovnbrrec_bridge_set_name(bridge, ctx->argv[1]);
> > +}
> > +
> > +static void
> > +cmd_br_del(struct ctl_context *ctx)
> > +{
> > +    const struct ovnbrrec_bridge *bridge = NULL;
> > +
> > +    char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &bridge);
> > +    if (error) {
> > +        ctl_error(ctx, "%s", error);
> > +        free(error);
> > +        return;
> > +    }
> > +
> > +    ovnbrrec_bridge_delete(bridge);
> > +}
> > +
> > +static void
> > +cmd_lflow_list(struct ctl_context *ctx)
> > +{
> > +    struct vector lflows = VECTOR_EMPTY_INITIALIZER(struct brctl_lflow);
> > +    const struct ovnbrrec_bridge *bridge = NULL;
> > +
> > +    if (ctx->argc > 1) {
> > +        char *error = br_by_name_or_uuid(ctx, ctx->argv[1], false, 
> > &bridge);
> > +        if (error) {
> > +            ctl_error(ctx, "%s", error);
> > +            free(error);
> > +            return;
> > +        }
> > +
> > +        if (bridge) {
> > +            ctx->argc--;
> > +            ctx->argv++;
> > +        }
> > +    }
> > +
> > +    const struct ovnbrrec_logical_flow *lflow;
> > +    OVNBRREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) {
> > +        if (bridge && lflow->bridge != bridge) {
> > +            continue;
> > +        }
> > +
> > +        brctl_lflow_add(&lflows, lflow, lflow->bridge);
> > +    }
> > +
> > +    vector_qsort(&lflows, brctl_lflow_cmp);
> > +
> > +    bool print_uuid = shash_find(&ctx->options, "--uuid") != NULL;
> > +
> > +    const struct brctl_lflow *curr, *prev = NULL;
> > +    VECTOR_FOR_EACH_PTR (&lflows, curr) {
> > +        /* Figure out whether to print this particular flow.  By default, 
> > we
> > +         * print all flows, but if any UUIDs were listed on the command 
> > line
> > +         * then we only print the matching ones. */
> > +        bool include;
> > +        if (ctx->argc > 1) {
> > +            include = false;
> > +            for (size_t j = 1; j < ctx->argc; j++) {
> > +                if (is_partial_uuid_match(&curr->lflow->header_.uuid,
> > +                                          ctx->argv[j])) {
> > +                    include = true;
> > +                    break;
> > +                }
> > +            }
> > +        } else {
> > +            include = true;
> > +        }
> > +        if (!include) {
> > +            continue;
> > +        }
> > +
> > +        /* Print a header line for this datapath or pipeline, if we haven't
> > +         * already done so. */
> > +        if (!prev || prev->br != curr->br) {
> > +               print_bridge_prompt(curr->br, &curr->br->header_.uuid,
> > +                                     &ctx->output);
> > +        }
> > +
> > +        /* Print the flow. */
> > +        ds_put_cstr(&ctx->output, "  ");
> > +        print_uuid_part(&curr->lflow->header_.uuid, print_uuid, 
> > &ctx->output);
> > +        ds_put_format(&ctx->output,
> > +                      "table=%-2"PRId64", priority=%-5"PRId64
> > +                      ", match=(%s), action=(%s)\n",
> > +                      curr->lflow->table_id,
> > +                      curr->lflow->priority, curr->lflow->match,
> > +                      curr->lflow->actions);
> > +        prev = curr;
> > +    }
> > +
> > +    vector_destroy(&lflows);
> > +}
> > +
> > +static void
> > +cmd_lflow_add(struct ctl_context *ctx)
> > +{
> > +    const struct ovnbrrec_bridge *bridge = NULL;
> > +
> > +    char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &bridge);
> > +    if (error) {
> > +        ctl_error(ctx, "%s", error);
> > +        free(error);
> > +        return;
> > +    }
> > +
> > +    int64_t table_id;
> > +    error = parse_table_id(ctx->argv[2], &table_id);
> > +    if (error) {
> > +        ctx->error = error;
> > +        return;
> > +    }
> > +
> > +    int64_t priority;
> > +    error = parse_priority(ctx->argv[3], &priority);
> > +    if (error) {
> > +        ctx->error = error;
> > +        return;
> > +    }
> > +
> > +    struct ovnbrrec_logical_flow *lflow =
> > +        ovnbrrec_logical_flow_insert(ctx->txn);
> > +    ovnbrrec_logical_flow_set_bridge(lflow, bridge);
> > +    ovnbrrec_logical_flow_set_table_id(lflow, table_id);
> > +    ovnbrrec_logical_flow_set_priority(lflow, priority);
> > +    ovnbrrec_logical_flow_set_match(lflow, ctx->argv[4]);
> > +    ovnbrrec_logical_flow_set_actions(lflow, ctx->argv[5]);
> > +}
> > +
> > +static void
> > +cmd_lflow_del(struct ctl_context *ctx)
> > +{
> > +    const struct ovnbrrec_logical_flow *lflow;
> > +    const struct ovsdb_idl_row *row;
> > +
> > +    char *error = ctl_get_row(ctx, &ovnbrrec_table_logical_flow,
> > +                              ctx->argv[1], true, &row);
> > +    if (error) {
> > +        ctl_error(ctx, "%s", error);
> > +        free(error);
> > +        return;
> > +    }
> > +
> > +    lflow = (const struct ovnbrrec_logical_flow *) row;
> > +    ovnbrrec_logical_flow_delete(lflow);
> > +}
> > +
> > +static void
> > +cmd_lflows_del(struct ctl_context *ctx)
> > +{
> > +    const struct ovnbrrec_bridge *bridge = NULL;
> > +
> > +    if (ctx->argc > 1) {
> > +        char *error = br_by_name_or_uuid(ctx, ctx->argv[1], true, &bridge);
> > +        if (error) {
> > +            ctl_error(ctx, "%s", error);
> > +            free(error);
> > +            return;
> > +        }
> > +    }
> > +
> > +    const struct ovnbrrec_logical_flow *lflow;
> > +    OVNBRREC_LOGICAL_FLOW_FOR_EACH_SAFE (lflow, ctx->idl) {
> > +        if (!bridge || lflow->bridge == bridge) {
> > +            ovnbrrec_logical_flow_delete(lflow);
> > +        }
> > +    }
> > +}
> > +
> > +static const struct ctl_command_syntax brctl_commands[] = {
> > +    { "init", 0, 0, "", NULL, brctl_init, NULL, "", RW },
> > +    { "show", 0, 1, "[BRIDGE]", brctl_pre_show, brctl_show, NULL, "", RO },
> > +
> > +    /* Bridge commands. */
> > +    {"add-br", 1, 1, "BRIDGE", pre_get_info, cmd_br_add, NULL,
> > +     "", RW},
> > +    {"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_br_del, NULL,
> > +     "", RW},
> > +
> > +    /* Logical flow commands */
> > +    {"lflow-list", 0, INT_MAX, "[BRIDGE] [LFLOW...]",
> > +     pre_get_info, cmd_lflow_list, NULL,
> > +     "--uuid,--ovs?,--stats,--vflows?", RO},
> > +    {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
> > +     pre_get_info, cmd_lflow_list, NULL,
> > +     "--uuid,--ovs?,--stats,--vflows?",
> > +     RO}, /* Friendly alias for lflow-list */
> > +    {"add-flow", 5, 5, "BRIDGE TABLE PRIORITY MATCH ACTION",
> > +     pre_get_info, cmd_lflow_add, NULL,
> > +     "", RW},
> > +     {"del-flow", 1, 1, "UUID", pre_get_info, cmd_lflow_del, NULL,
> > +     "", RW},
> > +     {"del-flows", 0, 1, "[BRIDGE]", pre_get_info, cmd_lflows_del, NULL,
> > +     "", RW},
> > +     {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
> > +};
> > +
> > +static void
> > +brctl_usage(void)
> > +{
> > +    printf("\
> > +%s: OVN Provider DB management utility\n\
> > +usage: %s [OPTIONS] COMMAND [ARG...]\n\
> > +\n\
> > +General commands:\n\
> > +  init                      initialize the database\n\
> > +  show                      print overview of database contents\n\
> > +\n\
> > +Logical flow commands:\n\
> > +  lflow-list  [BRIDGE] [LFLOW...] list logical flows for BRIDGE\n\
> > +  dump-flows  [BRIDGE] [LFLOW...] alias for lflow-list\n\
> > +\n\
> > +\n\n", program_name, program_name);
> > +}
> > +
> > +int
> > +main(int argc, char *argv[])
> > +{
> > +    struct ovn_dbctl_options dbctl_options = {
> > +        .db_version = ovnbrrec_get_db_version(),
> > +        .default_db = default_br_db(),
> > +        .allow_wait = false,
> > +
> > +        .options_env_var_name = "OVN_brctl_OPTIONS",
> > +        .daemon_env_var_name = "OVN_PR_DAEMON",
> > +
> > +        .idl_class = &ovnbrrec_idl_class,
> > +        .tables = tables,
> > +        .cmd_show_table = NULL,
> > +        .commands = brctl_commands,
> > +
> > +        .usage = brctl_usage,
> > +        .add_base_prerequisites = brctl_add_base_prerequisites,
> > +        .pre_execute = brctl_pre_execute,
> > +        .post_execute = NULL,
> > +        .get_inactivity_probe = get_inactivity_probe,
> > +
> > +        .ctx_create = brctl_ctx_create,
> > +        .ctx_destroy = brctl_ctx_destroy,
> > +    };
> > +
> > +    return ovn_dbctl_main(argc, argv, &dbctl_options);
> > +}
> > diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
> > index 0bba8f6889..c06bc435c4 100644
> > --- a/utilities/ovn-sbctl.c
> > +++ b/utilities/ovn-sbctl.c
> > @@ -624,12 +624,6 @@ sbctl_lflow_cmp(const void *a_, const void *b_)
> >      return cmp ? cmp : strcmp(a->actions, b->actions);
> >  }
> >
> > -static bool
> > -is_uuid_with_prefix(const char *uuid)
> > -{
> > -     return uuid[0] == '0' && (uuid[1] == 'x' || uuid[1] == 'X');
> > -}
> > -
> >  static bool
> >  parse_partial_uuid(char *s)
> >  {
> > @@ -648,30 +642,6 @@ parse_partial_uuid(char *s)
> >      return false;
> >  }
> >
> > -static const char *
> > -strip_leading_zero(const char *s)
> > -{
> > -    return s + strspn(s, "0");
> > -}
> > -
> > -static bool
> > -is_partial_uuid_match(const struct uuid *uuid, const char *match)
> > -{
> > -    char uuid_s[UUID_LEN + 1];
> > -    snprintf(uuid_s, sizeof uuid_s, UUID_FMT, UUID_ARGS(uuid));
> > -
> > -    /* We strip leading zeros because we want to accept cookie values 
> > derived
> > -     * from UUIDs, and cookie values are printed without leading zeros 
> > because
> > -     * they're just numbers. */
> > -    const char *s1 = strip_leading_zero(uuid_s);
> > -    const char *s2 = match;
> > -    if (is_uuid_with_prefix(s2)) {
> > -        s2 = s2 + 2;
> > -    }
> > -    s2 = strip_leading_zero(s2);
> > -    return !strncmp(s1, s2, strlen(s2));
> > -}
> > -
> >  static char *
> >  default_ovs(void)
> >  {
> > --
> > 2.51.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