On Wed, Sep 23, 2026 at 3:24 PM Jacob Tanenbaum <[email protected]> wrote:
> Currently ovn-controller controls the OpenFlow flows of the > internal bridge br-int through the default service controller > of the <bridge>.mgmt management port. That connection is not > visible to the bridge's fail-open/fail-secure state machine: > ofproto_is_alive() only tracks primary controllers, so OVS > cannot distinguish a live ovn-controller from a dead one. > > Register a primary OpenFlow controller on br-int pointing at a > passive listener, punix:<rundir>/br-int.ovn-primary, and make > the ofctrl, pinctrl, statctrl, and feature-discovery software > connections dial that endpoint instead of the .mgmt port. > Making the connection a primary controller lets the bridge's > fail-open/fail-secure state machine and ofproto_is_alive() > track the ovn-controller connection. > > The primary target is derived from the same endpoint > br_int_remote_update() dials, so the value of > external_ids:ovn-bridge-remote is honored. A primary > controller is a listener hosted by ovn-controller, which > only applies to local unix: and punix: endpoints; for remote > tcp: and ssl: endpoints the operator configures the primary > listener separately (e.g. with "ovs-vsctl set-controller"). > Passive inet targets use a "[<port>][:<host>]" layout, so > the active scheme cannot be swapped onto them. > > To avoid racing the socket creation, the first connection to > the primary endpoint is deferred until ovs-vswitchd applies > the commit that registers the controller, which is the change > that creates the punix listener. The Open_vSwitch next_cfg > and cur_cfg protocol, the same one ovs-vsctl --wait relies > on, is reused for this: when a new primary controller is > registered, next_cfg is incremented in the same transaction, > and all the software connections that dial the primary > endpoint are held back until cur_cfg advances past the > pre-increment value. The wait is only armed when a new > primary controller is inserted (local unix/punix endpoints); > when a matching controller already exists, or the endpoint is > remote (tcp/ssl), the connections dial immediately. > > Note that upgrading to this release adds the first primary > controller to an existing br-int, which causes OVS to flush > the OpenFlow tables; ovn-controller reinstalls them on the > next run. With fail_mode=secure, br-int traffic on that > chassis is dropped for the reinstall window. > > Reported-at: https://redhat.atlassian.net/browse/FDP-3771 > Assisted-by: Qwen3.8-27B-FP8, OpenCode > Signed-off-by: Jacob Tanenbaum <[email protected]> > > Assisted-by: Qwen3.8-27B-FP8, OpenCode > Signed-off-by: Jacob Tanenbaum <[email protected]> > > --- > v3 > * rebase issues with NEWS file > v2 > * next_cfg incrementing introduced to ensure that no race condition > exists by attempting to connect to a socket that does not exists. > Some strange issues with the server CA certificate Recheck-request: github-robot-_Build_and_Test > --- > NEWS | 16 +++ > controller/ovn-controller.8.xml | 21 +++- > controller/ovn-controller.c | 208 ++++++++++++++++++++++++++++++-- > ovn-architecture.7.xml | 19 +++ > tests/ovn-controller.at | 80 +++++++++++- > 5 files changed, 329 insertions(+), 15 deletions(-) > > diff --git a/NEWS b/NEWS > index f1c56dd14..67060cbbf 100644 > --- a/NEWS > +++ b/NEWS > @@ -1,5 +1,21 @@ > Post v26.09.0 > -------------- > + - ovn-controller now controls the OpenFlow flows of the br-int > + integration bridge through a primary OpenFlow controller that it > + registers on the bridge (target > "punix:<run_dir>/br-int.ovn-primary"), > + instead of the service controller of the br-int.mgmt management > + port. This lets the bridge's fail-open/fail-secure state machine > + track the ovn-controller connection. When > + external_ids:ovn-bridge-remote is a local unix: or punix: target, the > + primary controller listens on the same socket ovn-controller dials; > + for a remote tcp: or ssl: target the operator must configure the > + primary controller separately, e.g. with "ovs-vsctl > + set-controller br-int ptcp:<port>". > + - Note that upgrading to this release adds the first primary > + controller to an existing br-int, which causes OVS to flush the > + OpenFlow flow, group, and meter tables. ovn-controller reinstalls > + them on its next run; with fail_mode=secure, br-int traffic on the > + chassis is dropped for that window. > - Added a new "options:ttl" key on the NB DNS table to make the TTL of > DNS replies from OVN's native DNS resolver configurable per row. > - Removed implementations of the commit_ecmp_nh, chk_ecmp_nh, and > diff --git a/controller/ovn-controller.8.xml > b/controller/ovn-controller.8.xml > index 3c33654ff..311b51b21 100644 > --- a/controller/ovn-controller.8.xml > +++ b/controller/ovn-controller.8.xml > @@ -417,7 +417,26 @@ > <dd> > <p> > Connection to the OVN management bridge in OvS. It defaults to > - <code>unix:<var>br-int</var>.mgmt</code> when not specified. > + <code>unix:<var>br-int</var>.ovn-primary</code> when not > specified. > + This is the primary OpenFlow controller connection for the > + integration bridge; the usual > + <code>unix:<var>br-int</var>.mgmt</code> management socket > remains > + available to tools such as <code>ovs-ofctl</code>(8) and > + <code>ovs-appctl</code>(8). > + </p> > + <p> > + When this option is unset, or is a > + <code>unix:</code>/<code>punix:</code> target, the connection is > + registered on the bridge as a <code>primary</code> OpenFlow > + controller whose target is the passive (listening) > + <code>punix:</code> form of this endpoint, so that OVS > + fail-open/fail-secure tracks the <code>ovn-controller</code> > + connection. For <code>tcp:</code> or <code>ssl:</code> targets > the > + listener is not hosted by <code>ovn-controller</code>; the > primary > + OpenFlow controller (listener) must be configured separately, > e.g. > + with <code>ovs-vsctl set-controller</code>(8). In that case OVS > + fail-secure tracks that configured controller rather than one > created > + by <code>ovn-controller</code>. > </p> > </dd> > <dt><code>external_ids:ovn-bridge-remote-probe-interval</code></dt> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index c601f89dc..275d0ead8 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -145,6 +145,7 @@ static unixctl_cb_func debug_delay_nb_cfg_report; > #define OVS_NB_CFG_TS_NAME "ovn-nb-cfg-ts" > #define OVS_NB_CFG_SB_TS_NAME "ovn-nb-cfg-sb-ts" > #define OVS_STARTUP_TS_NAME "ovn-startup-ts" > +#define BR_INT_PRIMARY_CTRL_ID "ovn-br-int-primary" > > struct br_int_remote { > char *target; > @@ -655,7 +656,131 @@ get_br_datapath(const struct ovsrec_open_vswitch > *cfg, > return NULL; > } > > -static void > +/* Returns the primary Controller target ovn-controller should register > for > + * the integration bridge, or NULL if it should not register one. > 'remote' is > + * the value of external_ids:ovn-bridge-remote, or NULL when unset (in > which > + * case the default local "unix:<rundir>/<bridge>.ovn-primary" endpoint is > + * used). > + * > + * The primary Controller must be a listener that ovn-controller itself > hosts > + * on the same endpoint its software connection dials, so that OVS's > + * fail-open/fail-secure state machine (ofproto_is_alive()) can observe > the > + * ovn-controller connection. That is only possible for local endpoints: > + * - unix:/punix: (and the default) are local sockets ovn-controller can > + * listen on, so the passive "punix:<path>" form is registered. > + * - tcp:/ssl: (and ptcp:/pssl:) are remote endpoints; the listener is > not > + * hosted by ovn-controller (it dials the peer). The operator > configures > + * the primary controller (listener) separately, e.g. with "ovs-vsctl > + * set-controller", so nothing is registered here. Registering a > local > + * stand-in would be wrong (and, for inet targets, the passive form > uses a > + * "[<port>][:<host>]" layout that does not match "host:port"). > + */ > +static char * > +br_int_primary_target(const char *remote, const struct ovsrec_bridge > *br_int) > +{ > + if (!remote) { > + return xasprintf("punix:%s/%s.ovn-primary", ovs_rundir(), > + br_int->name); > + } > + if (!strncmp(remote, "unix:", 5) || !strncmp(remote, "punix:", 6)) { > + return xasprintf("punix:%s", strchr(remote, ':') + 1); > + } > + return NULL; > +} > + > +/* Ensure the integration bridge has a primary OpenFlow controller that > + * points at the passive listener ovn-controller connects through (see > + * br_int_remote_update()). Making that connection a *primary* controller > + * (as opposed to the service controller that OVS always adds internally > for > + * the <bridge>.mgmt port) causes the bridge's OpenFlow > fail-open/fail-secure > + * state machine and ofproto_is_alive() to track the ovn-controller > + * connection. A primary controller is registered only for local > + * (unix/punix) endpoints, for which ovn-controller hosts the listener; > for > + * remote (tcp/ssl) endpoints the operator configures the primary > controller > + * separately. This runs regardless of whether the bridge was just > created > + * or pre-existed (e.g. created by the distribution's startup scripts). > + * > + * When a new row is inserted, Open_vSwitch.next_cfg is incremented in the > + * same transaction. This marks the commit that introduces the > controller, > + * so the caller can defer the first connection to the primary endpoint > until > + * ovs-vswitchd has applied that commit (and thereby created the punix > + * listener). Returns true if a new row was inserted (and next_cfg > + * incremented), false when no row is inserted: either no primary target > + * applies (remote tcp/ssl endpoint) or a matching primary controller > already > + * exists, in which case ovs-vswitchd has already created the punix > listener > + * and no wait is needed. > + */ > +static bool > +process_br_int_primary_controller(struct ovsdb_idl_txn *ovs_idl_txn, > + const struct ovsrec_bridge *br_int, > + const struct ovsrec_open_vswitch *cfg) > +{ > + char *primary_target = > + br_int_primary_target( > + smap_get(&cfg->external_ids, "ovn-bridge-remote"), br_int); > + > + /* Drop any primary controller registered by a previous run that no > + * longer matches the configured endpoint (e.g. because > + * external_ids:ovn-bridge-remote was changed or removed, or the run > + * directory moved). Only rows created by ovn-controller -- tagged > with > + * BR_INT_PRIMARY_CTRL_ID in their external_ids -- are managed; > primary > + * controllers configured by the operator (e.g. "ovs-vsctl > + * set-controller") are left untouched. */ > + for (size_t i = 0; i < br_int->n_controller; i++) { > + const struct ovsrec_controller *c = br_int->controller[i]; > + /* c->type may be unset (NULL) for controllers configured by the > + * operator, so identify rows managed by ovn-controller by the > + * external_ids tag, which is safe to read, rather than by type. > */ > + if (c > + && smap_get_bool(&c->external_ids, BR_INT_PRIMARY_CTRL_ID, > false) > + && (!primary_target > + || strcmp(c->target, primary_target))) { > + ovsrec_bridge_update_controller_delvalue(br_int, c); > + ovsrec_controller_delete(c); > + } > + } > + if (!primary_target) { > + return false; > + } > + > + /* A primary controller for the configured endpoint already exists; > keep > + * it. This also covers a matching row created before this release > + * tagged it, so it is not duplicated. */ > + for (size_t i = 0; i < br_int->n_controller; i++) { > + const struct ovsrec_controller *c = br_int->controller[i]; > + if (c && !strcmp(c->target, primary_target) > + && c->type && !strcmp(c->type, "primary")) { > + free(primary_target); > + return false; > + } > + } > + > + struct ovsrec_controller *primary = > ovsrec_controller_insert(ovs_idl_txn); > + ovsrec_controller_set_target(primary, primary_target); > + ovsrec_controller_set_type(primary, "primary"); > + const struct smap ext_ids = > + SMAP_CONST1(&ext_ids, BR_INT_PRIMARY_CTRL_ID, "true"); > + ovsrec_controller_set_external_ids(primary, &ext_ids); > + free(primary_target); > + ovsrec_bridge_update_controller_addvalue(br_int, primary); > + > + /* Bump next_cfg in the same transaction as the insert so that > + * ovs-vswitchd's cur_cfg ack unambiguously covers the commit that > + * introduces this controller. ovn-controller defers the first > + * connection to the primary endpoint until that ack, which guarantees > + * the punix listener has been created before it is dialed. */ > + ovsdb_idl_txn_increment(ovs_idl_txn, &cfg->header_, > + &ovsrec_open_vswitch_col_next_cfg, false); > + return true; > +} > + > +/* Returns true when a new primary Controller row is inserted into > + * 'ovs_idl_txn' (which also increments Open_vSwitch next_cfg in the same > + * commit). The caller captures the pre-increment next_cfg value and > waits > + * until ovs-vswitchd's cur_cfg advances past it before connecting to the > + * primary endpoint, guaranteeing the punix listener has been created. > Returns > + * false when no row is inserted. */ > +static bool > process_br_int(struct ovsdb_idl_txn *ovs_idl_txn, > const struct ovsrec_bridge_table *bridge_table, > const struct ovsrec_open_vswitch_table *ovs_table, > @@ -663,6 +788,7 @@ process_br_int(struct ovsdb_idl_txn *ovs_idl_txn, > const struct ovsrec_datapath **br_int_dp) > { > const struct ovsrec_bridge *br_int = get_br_int(bridge_table, > ovs_table); > + bool primary_ctl_inserted = false; > > ovs_assert(br_int_); > if (ovs_idl_txn) { > @@ -699,6 +825,8 @@ process_br_int(struct ovsdb_idl_txn *ovs_idl_txn, > ovsrec_bridge_set_fail_mode(br_int, "secure"); > VLOG_WARN("Integration bridge fail-mode changed to > 'secure'."); > } > + primary_ctl_inserted = process_br_int_primary_controller( > + ovs_idl_txn, br_int, cfg); > if (br_int_dp) { > *br_int_dp = get_br_datapath(cfg, datapath_type); > if (!(*br_int_dp)) { > @@ -711,6 +839,7 @@ process_br_int(struct ovsdb_idl_txn *ovs_idl_txn, > } > } > *br_int_ = br_int; > + return primary_ctl_inserted; > } > > static void > @@ -983,6 +1112,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > 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_cur_cfg); > + ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_next_cfg); > ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); > ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); > ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); > @@ -992,6 +1123,11 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_flow_tables); > ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_other_config); > ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_external_ids); > + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_controller); > + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_controller); > + ovsdb_idl_add_column(ovs_idl, &ovsrec_controller_col_target); > + ovsdb_idl_add_column(ovs_idl, &ovsrec_controller_col_type); > + ovsdb_idl_add_column(ovs_idl, &ovsrec_controller_col_external_ids); > ovsdb_idl_add_table(ovs_idl, &ovsrec_table_flow_table); > ovsdb_idl_add_column(ovs_idl, &ovsrec_flow_table_col_prefixes); > ovsdb_idl_add_table(ovs_idl, &ovsrec_table_ssl); > @@ -7820,7 +7956,7 @@ br_int_remote_update(struct br_int_remote *remote, > smap_get(&cfg->external_ids, "ovn-bridge-remote"); > char *target = ext_target > ? xstrdup(ext_target) > - : xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name); > + : xasprintf("unix:%s/%s.ovn-primary", ovs_rundir(), > br_int->name); > > if (!remote->target || strcmp(remote->target, target)) { > free(remote->target); > @@ -8182,6 +8318,17 @@ main(int argc, char *argv[]) > VLOG_INFO("OVN internal version is : [%s]", ovn_version); > > /* Main loop. */ > + /* Set when process_br_int_primary_controller() inserts a new primary > + * controller (and bumps Open_vSwitch next_cfg in the same commit). > + * While set, the software connections to the primary endpoint are > held > + * back until ovs-vswitchd has applied that commit, which is the > change > + * that creates the punix listener. The wait is cleared once cur_cfg > + * advances past the pre-increment next_cfg value captured when the > + * controller was inserted -- the same ack protocol ovs-vsctl --wait > + * relies on. It is only set for local (unix/punix) endpoints, where > + * ovn-controller registers and hosts the primary listener. */ > + bool primary_ctl_wait = false; > + int64_t primary_ctl_base = 0; > int ovnsb_txn_status = 1; > struct tracked_acl_ids *tracked_acl_ids = NULL; > while (!exit_args.exiting) { > @@ -8273,15 +8420,17 @@ main(int argc, char *argv[]) > const struct ovsrec_datapath *br_int_dp = NULL; > const struct ovsrec_open_vswitch *cfg = > ovsrec_open_vswitch_table_first(ovs_table); > - process_br_int(ovs_idl_txn, bridge_table, ovs_table, &br_int, > - ovsrec_server_has_datapath_table(ovs_idl_loop.idl) > - ? &br_int_dp > - : NULL); > - br_int_remote_update(&br_int_remote, br_int, ovs_table); > - statctrl_update_swconn(br_int_remote.target, > - br_int_remote.probe_interval); > - pinctrl_update_swconn(br_int_remote.target, > - br_int_remote.probe_interval); > + > + /* ovs-vswitchd has applied the commit that introduced the primary > + * controller (cur_cfg advanced past the pre-increment next_cfg > that > + * was in force when the controller was inserted). The punix > + * listener has been created, so the software connections may dial > + * it. On a reconnection this check still holds, because cur_cfg > + * only increases and was at most primary_ctl_base when it was > + * captured. */ > + if (primary_ctl_wait && cfg && cfg->cur_cfg > primary_ctl_base) { > + primary_ctl_wait = false; > + } > > /* Enable ACL matching for double tagged traffic. */ > if (ovs_idl_txn && cfg) { > @@ -8311,6 +8460,37 @@ main(int argc, char *argv[]) > } > } > > + /* If a new primary controller was just registered, record the > + * pre-increment next_cfg so the software connections can be held > + * back until ovs-vswitchd applies the commit that creates the > + * punix listener. */ > + bool inserted_primary_ctl = process_br_int( > + ovs_idl_txn, bridge_table, ovs_table, &br_int, > + ovsrec_server_has_datapath_table(ovs_idl_loop.idl) > + ? &br_int_dp > + : NULL); > + if (inserted_primary_ctl && cfg) { > + primary_ctl_wait = true; > + primary_ctl_base = cfg->next_cfg; > + } > + > + /* All the software connections dial the endpoint ovn-controller > + * manages. While the punix listener has not been confirmed > created > + * (primary_ctl_wait), pass NULL so they are held back (or > + * disconnected) instead of dialing. On the first pass > + * br_int_remote.target is still NULL, so no connection is dialed > + * before it is set. */ > + const char *swconn_target = primary_ctl_wait > + ? NULL > + : br_int_remote.target; > + if (br_int && swconn_target) { > + statctrl_update_swconn(swconn_target, > + br_int_remote.probe_interval); > + pinctrl_update_swconn(swconn_target, > + br_int_remote.probe_interval); > + } > + br_int_remote_update(&br_int_remote, br_int, ovs_table); > + > static bool chassis_idx_stored = false; > if (ovs_idl_txn && !chassis_idx_stored) { > store_chassis_index_if_needed(ovs_table); > @@ -8349,11 +8529,13 @@ main(int argc, char *argv[]) > > /* If any OVS feature support changed, force a full recompute. > * 'br_int_dp' is valid only if an OVS transaction is > possible. > + * While the primary listener has not been confirmed created, > + * pass NULL to disconnect instead of dialing it. > */ > if (ovs_idl_txn > && ovs_feature_support_run(br_int_dp ? > &br_int_dp->capabilities : > NULL, > - br_int_remote.target, > + swconn_target, > br_int_remote.probe_interval)) > { > VLOG_INFO("OVS feature set changed, force recompute."); > engine_set_force_recompute(); > @@ -8369,7 +8551,7 @@ main(int argc, char *argv[]) > > if (br_int) { > ct_zones_data = engine_get_data(&en_ct_zones); > - if (ofctrl_run(br_int_remote.target, > + if (ofctrl_run(swconn_target, > br_int_remote.probe_interval, > ct_zones_data ? &ct_zones_data->ctx.pending > : NULL, > diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml > index 00728ee9b..3022be9f2 100644 > --- a/ovn-architecture.7.xml > +++ b/ovn-architecture.7.xml > @@ -381,6 +381,25 @@ > Settings</code> in <code>ovs-vsctl</code>(8) for more information. > </dd> > > + <dt>a <code>primary</code> <code>Controller</code></dt> > + <dd> > + <code>ovn-controller</code> connects to the integration bridge > through a > + <code>Controller</code> row of type <code>primary</code> rather than > + through the service controller that OVS always adds internally for > the > + <code>br-int.mgmt</code> port. The row's target is the passive > listener > + <code>punix:<var>run_dir</var>/br-int.ovn-primary</code> and the > + <code>ovn-controller</code> OpenFlow connections dial the > corresponding > + <code>unix:</code> form of the same path. Because the connection > is a > + <em>primary</em> controller, the bridge's OpenFlow > fail-open/fail-secure > + state machine and <code>ofproto_is_alive()</code> track the > + <code>ovn-controller</code> connection. When > + <code>external_ids:ovn-bridge-remote</code> names a local > + <code>unix:</code> or <code>punix:</code> target, the listener is > + created on that path instead; for a remote <code>tcp:</code> or > + <code>ssl:</code> target the operator configures the primary > controller > + separately, e.g. with <code>ovs-vsctl set-controller</code>(8). > + </dd> > + > <dt><code>other-config:disable-in-band=true</code></dt> > <dd> > Suppresses in-band control flows for the integration bridge. It > would be > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at > index a7b79fc67..0b0080a0a 100644 > --- a/tests/ovn-controller.at > +++ b/tests/ovn-controller.at > @@ -3319,6 +3319,83 @@ OVS_WAIT_UNTIL([grep -q 'tcp:127.0.0.1:1235: > connected' hv1/ovn-controller.log]) > OVN_CLEANUP([hv1]) > AT_CLEANUP > > +AT_SETUP([ovn-controller - br-int primary controller]) > +AT_KEYWORDS([ovn]) > +ovn_start > + > +net_add n1 > +sim_add hv1 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.20 > + > +# ovn-controller registers itself as a primary OpenFlow controller of the > +# integration bridge, so OVS fail-open/fail-secure tracks the > +# ovn-controller connection. This holds whether the bridge was just > created > +# or pre-existed (ovn_attach pre-creates br-int here). > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=type --bare list Controller) > = "xprimary"]) > +AT_CHECK([ovs-vsctl --columns=type --bare list Controller], [0], [primary > +]) > +# The bridge keeps its fail-mode=secure setting. > +AT_CHECK([ovs-vsctl --columns=fail_mode --bare list bridge br-int], [0], > [secure > +]) > + > +# ovn-controller connects through the primary (not the default .mgmt) > socket. > +OVS_WAIT_UNTIL([grep -q 'connecting to switch: > "unix:.*br-int\.ovn-primary"' hv1/ovn-controller.log]) > +OVS_WAIT_UNTIL([grep -q 'br-int\.ovn-primary: connected' > hv1/ovn-controller.log]) > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > + > +AT_SETUP([ovn-controller - br-int primary controller with > ovn-bridge-remote]) > +AT_KEYWORDS([ovn]) > +ovn_start > + > +net_add n1 > +sim_add hv1 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.20 > + > +# With external_ids:ovn-bridge-remote unset, ovn-controller registers the > +# default primary controller on the default local listener. > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=target --bare find Controller > type=primary) = "xpunix:$ovs_base/hv1/br-int.ovn-primary"]) > + > +# A local unix: override: the primary controller moves to the passive > form of > +# the overridden path, and the previous (stale) primary is removed. > +check ovs-vsctl set open . > external_ids:ovn-bridge-remote=unix:$ovs_base/hv1/br-int.ovn-override > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=target --bare find Controller > type=primary) = "xpunix:$ovs_base/hv1/br-int.ovn-override"]) > + > +# Remove the local override: ovn-controller restores the default primary. > +check ovs-vsctl remove open . external_ids ovn-bridge-remote > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=target --bare find Controller > type=primary) = "xpunix:$ovs_base/hv1/br-int.ovn-primary"]) > + > +# A remote tcp: override: the operator configures the primary listener > +# (separately, since ovn-controller cannot host a remote listener) and > then > +# points the override at it. ovn-controller dials the endpoint, removes > the > +# stale local primary, and registers no primary controller of its own; the > +# operator-configured controller (which has no type) is left alone. > +check ovs-vsctl set-controller br-int ptcp:1276 > +# "set-controller" wiped the controllers; with the override unset, > +# ovn-controller re-registers the default primary. > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=target --bare find Controller > type=primary) = "xpunix:$ovs_base/hv1/br-int.ovn-primary"]) > +check ovs-vsctl set open . external_ids:ovn-bridge-remote=tcp: > 127.0.0.1:1276 > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=target --bare find > Controller) = "xptcp:1276"]) > +OVS_WAIT_UNTIL([grep -q 'tcp:127.0.0.1:1276: connected' > hv1/ovn-controller.log]) > + > +# Removing the override: ovn-controller registers the default primary > again, > +# keeping the operator-configured controller, and the swconn reconnects > +# through the default socket. > +check ovs-vsctl remove open . external_ids ovn-bridge-remote > +OVS_WAIT_UNTIL([test x$(ovs-vsctl --columns=target --bare find Controller > type=primary) = "xpunix:$ovs_base/hv1/br-int.ovn-primary"]) > +OVS_WAIT_UNTIL([test $(grep -c 'ofctrl: connecting to switch: > "unix:'$ovs_base'/hv1/br-int.ovn-primary"' hv1/ovn-controller.log) -ge 2]) > +# Both controllers remain: the operator-configured one and the default > +# primary. "find --bare" separates rows with a blank line, so count the > +# non-empty lines. > +OVS_WAIT_UNTIL([test 2 = $(ovs-vsctl --columns=target --bare find > Controller | grep -c .)]) > +OVS_WAIT_UNTIL([ovs-vsctl --columns=target --bare find Controller | grep > -qx ptcp:1276]) > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > + > AT_SETUP([ovn-controller - br-int flow table prefixes]) > AT_KEYWORDS([ovn-controller prefixes]) > ovn_start > @@ -3664,8 +3741,9 @@ AT_CHECK([ovsdb-client --bare dump unix:db.sock > Open_vSwitch Open_vSwitch], [], > Open_vSwitch table > ]) > check ovs-vsctl --no-wait init > -OVS_WAIT_FOR_OUTPUT([ovs-vsctl show | tail -n +2], [], [dnl > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovs-vsctl show | tail -n +2], [], [dnl > Bridge br-int > + Controller "punix:$(pwd)/br-int.ovn-primary" > fail_mode: secure > datapath_type: system > Port br-int > -- > 2.55.0 > > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
