ebb439
From f1b715c3f0f222cbf6bf07733792a16c6442a085 Mon Sep 17 00:00:00 2001
ebb439
From: Numan Siddique <numans@ovn.org>
ebb439
Date: Fri, 13 Nov 2020 23:44:10 +0530
ebb439
Subject: [PATCH 01/16] Provide the option to pin ovn-controller and ovn-northd
ebb439
 to a specific version.
ebb439
ebb439
OVN recommends updating/upgrading ovn-controllers first and then
ebb439
ovn-northd and OVN DB ovsdb-servers.  This is to ensure that any
ebb439
new functionality specified by the database or logical flows created
ebb439
by ovn-northd is understood by ovn-controller.
ebb439
ebb439
However certain deployments may upgrade ovn-northd services first and
ebb439
then ovn-controllers.  In a large scal deployment, this can result in
ebb439
downtime during upgrades as old ovn-controllers may not understand
ebb439
new logical flows or new actions added by ovn-northd.
ebb439
ebb439
Even upgrading ovn-controllers first can result in ovn-controllers
ebb439
rejecting some of the logical flows if an existing OVN action is
ebb439
changed.  One such example is ct_commit action which recently was updated
ebb439
to take new arguments.
ebb439
ebb439
To avoid such downtimes during upgrades, this patch adds the
ebb439
functionality of pinning ovn-controller and ovn-northd to a specific
ebb439
version.  An internal OVN version is generated and this version is stored
ebb439
by ovn-northd in the Southbound SB_Global table's
ebb439
options:northd_internal_version.
ebb439
ebb439
When ovn-controller notices that the internal version has changed, it
ebb439
stops handling the database changes - both Southbound and OVS. All the
ebb439
existing OF flows are preserved.  When ovn-controller is upgraded to the
ebb439
same version as ovn-northd services, it will process the database
ebb439
changes.
ebb439
ebb439
This feature is made optional and disabled by default.  A CMS can
ebb439
enable it by configuring the OVS local database with the option -
ebb439
ovn-match-northd-version=true.
ebb439
ebb439
Change-Id: I5f6c693953db1a5532b62cf0a7d588f78bb353c1
ebb439
Acked-by: Mark Michelson <mmichels@redhat.com>
ebb439
Signed-off-by: Numan Siddique <numans@ovn.org>
ebb439
---
ebb439
 .../contributing/submitting-patches.rst       |  12 ++
ebb439
 NEWS                                          |   3 +
ebb439
 controller/ovn-controller.8.xml               |  11 ++
ebb439
 controller/ovn-controller.c                   |  52 ++++++-
ebb439
 include/ovn/actions.h                         |   4 +
ebb439
 lib/ovn-util.c                                |  14 ++
ebb439
 lib/ovn-util.h                                |   4 +
ebb439
 northd/ovn-northd.c                           |  18 ++-
ebb439
 tests/ovn.at                                  | 147 ++++++++++++++++++
ebb439
 9 files changed, 260 insertions(+), 5 deletions(-)
ebb439
ebb439
diff --git a/Documentation/internals/contributing/submitting-patches.rst b/Documentation/internals/contributing/submitting-patches.rst
ebb439
index 0a9de5866..31a3ca747 100644
ebb439
--- a/Documentation/internals/contributing/submitting-patches.rst
ebb439
+++ b/Documentation/internals/contributing/submitting-patches.rst
ebb439
@@ -397,6 +397,18 @@ Remember to follow-up and actually remove the feature from OVN codebase once
ebb439
 deprecation grace period has expired and users had opportunity to use at least
ebb439
 one OVN release that would have informed them about feature deprecation!
ebb439
 
ebb439
+OVN upgrades
ebb439
+------------
ebb439
+
ebb439
+If the patch introduces any new OVN actions or updates existing OVN actions,
ebb439
+then make sure to check the function ovn_get_internal_version() in
ebb439
+lib/ovn-util.c and increment the macro - OVN_INTERNAL_MINOR_VER.
ebb439
+
ebb439
+Adding new OVN actions or changing existing OVN actions can have datapath
ebb439
+disruptions during OVN upgrades. To minimize disruptions, OVN supports
ebb439
+version matching between ovn-northd and ovn-controller and it is important
ebb439
+to update the internal OVN version when the patch introduces such changes.
ebb439
+
ebb439
 Comments
ebb439
 --------
ebb439
 
ebb439
diff --git a/NEWS b/NEWS
ebb439
index 46140208f..5968ca341 100644
ebb439
--- a/NEWS
ebb439
+++ b/NEWS
ebb439
@@ -2,6 +2,9 @@ Post-v20.09.0
ebb439
 ---------------------
ebb439
    - Support other_config:vlan-passthru=true to allow VLAN tagged incoming
ebb439
      traffic.
ebb439
+   - Support version pinning between ovn-northd and ovn-controller as an
ebb439
+     option. If the option is enabled and the versions don't match,
ebb439
+     ovn-controller will not process any DB changes.
ebb439
 
ebb439
 OVN v20.09.0 - 28 Sep 2020
ebb439
 --------------------------
ebb439
diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml
ebb439
index 16bc47b20..b5c0800f0 100644
ebb439
--- a/controller/ovn-controller.8.xml
ebb439
+++ b/controller/ovn-controller.8.xml
ebb439
@@ -233,6 +233,17 @@
ebb439
         The boolean flag indicates if the chassis is used as an
ebb439
         interconnection gateway.
ebb439
       
ebb439
+
ebb439
+      
external_ids:ovn-match-northd-version
ebb439
+      
ebb439
+        The boolean flag indicates if ovn-controller needs to
ebb439
+        check ovn-northd version. If this
ebb439
+        flag is set to true and the ovn-northd's version (reported
ebb439
+        in the Southbound database) doesn't match with the
ebb439
+        ovn-controller's internal version, then it will stop
ebb439
+        processing the southbound and local Open vSwitch database changes.
ebb439
+        The default value is considered false if this option is not defined.
ebb439
+      
ebb439
     
ebb439
 
ebb439
     

ebb439
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
ebb439
index e5479cf3e..faae787f3 100644
ebb439
--- a/controller/ovn-controller.c
ebb439
+++ b/controller/ovn-controller.c
ebb439
@@ -2159,6 +2159,49 @@ struct ovn_controller_exit_args {
ebb439
     bool *restart;
ebb439
 };
ebb439
 
ebb439
+/* Returns false if the northd internal version stored in SB_Global
ebb439
+ * and ovn-controller internal version don't match.
ebb439
+ */
ebb439
+static bool
ebb439
+check_northd_version(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
ebb439
+                     const char *version)
ebb439
+{
ebb439
+    static bool version_mismatch;
ebb439
+
ebb439
+    const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
ebb439
+    if (!cfg || !smap_get_bool(&cfg->external_ids, "ovn-match-northd-version",
ebb439
+                               false)) {
ebb439
+        version_mismatch = false;
ebb439
+        return true;
ebb439
+    }
ebb439
+
ebb439
+    const struct sbrec_sb_global *sb = sbrec_sb_global_first(ovnsb_idl);
ebb439
+    if (!sb) {
ebb439
+        version_mismatch = true;
ebb439
+        return false;
ebb439
+    }
ebb439
+
ebb439
+    const char *northd_version =
ebb439
+        smap_get_def(&sb->options, "northd_internal_version", "");
ebb439
+
ebb439
+    if (strcmp(northd_version, version)) {
ebb439
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
ebb439
+        VLOG_WARN_RL(&rl, "controller version - %s mismatch with northd "
ebb439
+                     "version - %s", version, northd_version);
ebb439
+        version_mismatch = true;
ebb439
+        return false;
ebb439
+    }
ebb439
+
ebb439
+    /* If there used to be a mismatch and ovn-northd got updated, force a
ebb439
+     * full recompute.
ebb439
+     */
ebb439
+    if (version_mismatch) {
ebb439
+        engine_set_force_recompute(true);
ebb439
+    }
ebb439
+    version_mismatch = false;
ebb439
+    return true;
ebb439
+}
ebb439
+
ebb439
 int
ebb439
 main(int argc, char *argv[])
ebb439
 {
ebb439
@@ -2453,6 +2496,9 @@ main(int argc, char *argv[])
ebb439
         .enable_lflow_cache = true
ebb439
     };
ebb439
 
ebb439
+    char *ovn_version = ovn_get_internal_version();
ebb439
+    VLOG_INFO("OVN internal version is : [%s]", ovn_version);
ebb439
+
ebb439
     /* Main loop. */
ebb439
     exiting = false;
ebb439
     restart = false;
ebb439
@@ -2506,7 +2552,9 @@ main(int argc, char *argv[])
ebb439
 
ebb439
         engine_set_context(&eng_ctx);
ebb439
 
ebb439
-        if (ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl)) {
ebb439
+        if (ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl) &&
ebb439
+            check_northd_version(ovs_idl_loop.idl, ovnsb_idl_loop.idl,
ebb439
+                                 ovn_version)) {
ebb439
             /* Contains the transport zones that this Chassis belongs to */
ebb439
             struct sset transport_zones = SSET_INITIALIZER(&transport_zones);
ebb439
             sset_from_delimited_string(&transport_zones,
ebb439
@@ -2795,6 +2843,7 @@ loop_done:
ebb439
         }
ebb439
     }
ebb439
 
ebb439
+    free(ovn_version);
ebb439
     unixctl_server_destroy(unixctl);
ebb439
     lflow_destroy();
ebb439
     ofctrl_destroy();
ebb439
@@ -2847,6 +2896,7 @@ parse_options(int argc, char *argv[])
ebb439
 
ebb439
         case 'V':
ebb439
             ovs_print_version(OFP15_VERSION, OFP15_VERSION);
ebb439
+            printf("SB DB Schema %s\n", sbrec_get_db_version());
ebb439
             exit(EXIT_SUCCESS);
ebb439
 
ebb439
         VLOG_OPTION_HANDLERS
ebb439
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
ebb439
index 630bbe79e..7ba24cd60 100644
ebb439
--- a/include/ovn/actions.h
ebb439
+++ b/include/ovn/actions.h
ebb439
@@ -48,6 +48,10 @@ struct ovn_extend_table;
ebb439
  *    action.  Its first member must have type "struct ovnact" and name
ebb439
  *    "ovnact".  The structure must have a fixed length, that is, it may not
ebb439
  *    end with a flexible array member.
ebb439
+ *
ebb439
+ * These OVNACTS are used to generate the OVN internal version.   See
ebb439
+ * ovn_get_internal_version() in lib/ovn-util.c.  If any OVNACT is updated,
ebb439
+ * increment the OVN_INTERNAL_MINOR_VER macro in lib/ovn-util.c.
ebb439
  */
ebb439
 #define OVNACTS                                       \
ebb439
     OVNACT(OUTPUT,            ovnact_null)            \
ebb439
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
ebb439
index 8722f7a48..e94a16422 100644
ebb439
--- a/lib/ovn-util.c
ebb439
+++ b/lib/ovn-util.c
ebb439
@@ -20,6 +20,7 @@
ebb439
 #include <unistd.h>
ebb439
 
ebb439
 #include "daemon.h"
ebb439
+#include "include/ovn/actions.h"
ebb439
 #include "openvswitch/ofp-parse.h"
ebb439
 #include "openvswitch/vlog.h"
ebb439
 #include "ovn-dirs.h"
ebb439
@@ -720,3 +721,16 @@ ip_address_and_port_from_lb_key(const char *key, char **ip_address,
ebb439
     *addr_family = ss.ss_family;
ebb439
     return true;
ebb439
 }
ebb439
+
ebb439
+/* Increment this for any logical flow changes or if existing OVN action is
ebb439
+ * modified. */
ebb439
+#define OVN_INTERNAL_MINOR_VER 0
ebb439
+
ebb439
+/* Returns the OVN version. The caller must free the returned value. */
ebb439
+char *
ebb439
+ovn_get_internal_version(void)
ebb439
+{
ebb439
+    return xasprintf("%s-%s-%d.%d", OVN_PACKAGE_VERSION,
ebb439
+                     sbrec_get_db_version(),
ebb439
+                     N_OVNACTS, OVN_INTERNAL_MINOR_VER);
ebb439
+}
ebb439
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
ebb439
index f72a801df..aa737a20c 100644
ebb439
--- a/lib/ovn-util.h
ebb439
+++ b/lib/ovn-util.h
ebb439
@@ -231,4 +231,8 @@ char *str_tolower(const char *orig);
ebb439
 bool ip_address_and_port_from_lb_key(const char *key, char **ip_address,
ebb439
                                      uint16_t *port, int *addr_family);
ebb439
 
ebb439
+/* Returns the internal OVN version. The caller must free the returned
ebb439
+ * value. */
ebb439
+char *ovn_get_internal_version(void);
ebb439
+
ebb439
 #endif
ebb439
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
ebb439
index bb31e04fa..3884e08eb 100644
ebb439
--- a/northd/ovn-northd.c
ebb439
+++ b/northd/ovn-northd.c
ebb439
@@ -11939,7 +11939,8 @@ ovnnb_db_run(struct northd_context *ctx,
ebb439
              struct ovsdb_idl_loop *sb_loop,
ebb439
              struct hmap *datapaths, struct hmap *ports,
ebb439
              struct ovs_list *lr_list,
ebb439
-             int64_t loop_start_time)
ebb439
+             int64_t loop_start_time,
ebb439
+             const char *ovn_internal_version)
ebb439
 {
ebb439
     if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
ebb439
         return;
ebb439
@@ -12016,6 +12017,8 @@ ovnnb_db_run(struct northd_context *ctx,
ebb439
     smap_replace(&options, "max_tunid", max_tunid);
ebb439
     free(max_tunid);
ebb439
 
ebb439
+    smap_replace(&options, "northd_internal_version", ovn_internal_version);
ebb439
+
ebb439
     nbrec_nb_global_verify_options(nb);
ebb439
     nbrec_nb_global_set_options(nb, &options);
ebb439
 
ebb439
@@ -12626,7 +12629,8 @@ ovnsb_db_run(struct northd_context *ctx,
ebb439
 static void
ebb439
 ovn_db_run(struct northd_context *ctx,
ebb439
            struct ovsdb_idl_index *sbrec_chassis_by_name,
ebb439
-           struct ovsdb_idl_loop *ovnsb_idl_loop)
ebb439
+           struct ovsdb_idl_loop *ovnsb_idl_loop,
ebb439
+           const char *ovn_internal_version)
ebb439
 {
ebb439
     struct hmap datapaths, ports;
ebb439
     struct ovs_list lr_list;
ebb439
@@ -12636,7 +12640,8 @@ ovn_db_run(struct northd_context *ctx,
ebb439
 
ebb439
     int64_t start_time = time_wall_msec();
ebb439
     ovnnb_db_run(ctx, sbrec_chassis_by_name, ovnsb_idl_loop,
ebb439
-                 &datapaths, &ports, &lr_list, start_time);
ebb439
+                 &datapaths, &ports, &lr_list, start_time,
ebb439
+                 ovn_internal_version);
ebb439
     ovnsb_db_run(ctx, ovnsb_idl_loop, &ports, start_time);
ebb439
     destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);
ebb439
 }
ebb439
@@ -13003,6 +13008,9 @@ main(int argc, char *argv[])
ebb439
     unixctl_command_register("sb-connection-status", "", 0, 0,
ebb439
                              ovn_conn_show, ovnsb_idl_loop.idl);
ebb439
 
ebb439
+    char *ovn_internal_version = ovn_get_internal_version();
ebb439
+    VLOG_INFO("OVN internal version is : [%s]", ovn_internal_version);
ebb439
+
ebb439
     /* Main loop. */
ebb439
     exiting = false;
ebb439
     state.had_lock = false;
ebb439
@@ -13044,7 +13052,8 @@ main(int argc, char *argv[])
ebb439
             }
ebb439
 
ebb439
             if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
ebb439
-                ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop);
ebb439
+                ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop,
ebb439
+                           ovn_internal_version);
ebb439
                 if (ctx.ovnsb_txn) {
ebb439
                     check_and_add_supported_dhcp_opts_to_sb_db(&ctx;;
ebb439
                     check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx;;
ebb439
@@ -13106,6 +13115,7 @@ main(int argc, char *argv[])
ebb439
         }
ebb439
     }
ebb439
 
ebb439
+    free(ovn_internal_version);
ebb439
     unixctl_server_destroy(unixctl);
ebb439
     ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
ebb439
     ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
ebb439
diff --git a/tests/ovn.at b/tests/ovn.at
ebb439
index 8f18ca9e5..f771b7563 100644
ebb439
--- a/tests/ovn.at
ebb439
+++ b/tests/ovn.at
ebb439
@@ -23235,3 +23235,150 @@ OVS_WAIT_UNTIL(
ebb439
 
ebb439
 OVN_CLEANUP([hv1], [hv2])
ebb439
 AT_CLEANUP
ebb439
+
ebb439
+AT_SETUP([ovn -- check ovn-northd and ovn-controller version pinning])
ebb439
+ovn_start
ebb439
+
ebb439
+net_add n1
ebb439
+sim_add hv1
ebb439
+as hv1
ebb439
+ovs-vsctl add-br br-phys
ebb439
+ovn_attach n1 br-phys 192.168.0.10
ebb439
+
ebb439
+check ovn-nbctl ls-add sw0
ebb439
+check ovn-nbctl lsp-add sw0 sw0-p1
ebb439
+check ovn-nbctl lsp-add sw0 sw0-p2
ebb439
+
ebb439
+as hv1
ebb439
+ovs-vsctl \
ebb439
+    -- add-port br-int vif1 \
ebb439
+    -- set Interface vif1 external_ids:iface-id=sw0-p1 \
ebb439
+    ofport-request=1
ebb439
+ovs-vsctl \
ebb439
+    -- add-port br-int vif2 \
ebb439
+    -- set Interface vif2 external_ids:iface-id=sw0-p2 \
ebb439
+    ofport-request=2
ebb439
+
ebb439
+# Wait for port to be bound.
ebb439
+wait_row_count Chassis 1 name=hv1
ebb439
+ch=$(fetch_column Chassis _uuid name=hv1)
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p1 chassis=$ch
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p2 chassis=$ch
ebb439
+
ebb439
+northd_version=$(ovn-sbctl get SB_Global . options:northd_internal_version | sed s/\"//g)
ebb439
+echo "northd version = $northd_version"
ebb439
+AT_CHECK([grep -c $northd_version hv1/ovn-controller.log], [0], [1
ebb439
+])
ebb439
+
ebb439
+# Stop ovn-northd so that we can modify the northd_version.
ebb439
+as northd
ebb439
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
ebb439
+
ebb439
+as northd-backup
ebb439
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
ebb439
+
ebb439
+check ovn-sbctl set SB_Global . options:northd_internal_version=foo
ebb439
+
ebb439
+as hv1
ebb439
+check ovs-vsctl set interface vif2 external_ids:iface-id=foo
ebb439
+
ebb439
+# ovn-controller should release the lport sw0-p2 since ovn-match-northd-version
ebb439
+# is not true.
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p2 'chassis=[[]]'
ebb439
+
ebb439
+as hv1 ovs-ofctl dump-flows br-int table=0 > offlows_table0.txt
ebb439
+AT_CAPTURE_FILE([offlows_table0.txt])
ebb439
+AT_CHECK_UNQUOTED([grep -c "in_port=2" offlows_table0.txt], [1], [dnl
ebb439
+0
ebb439
+])
ebb439
+
ebb439
+echo
ebb439
+echo "__file__:__line__: Pin ovn-controller to ovn-northd version."
ebb439
+
ebb439
+as hv1
ebb439
+check ovs-vsctl set open . external_ids:ovn-match-northd-version=true
ebb439
+
ebb439
+OVS_WAIT_UNTIL(
ebb439
+    [test 1 = $(grep -c "controller version - $northd_version mismatch with northd version - foo" hv1/ovn-controller.log)
ebb439
+])
ebb439
+
ebb439
+as hv1
ebb439
+check ovs-vsctl set interface vif2 external_ids:iface-id=sw0-p2
ebb439
+
ebb439
+# ovn-controller should not claim sw0-p2 since there is version mismatch
ebb439
+as hv1 ovn-appctl -t ovn-controller recompute
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p2 'chassis=[[]]'
ebb439
+
ebb439
+as hv1 ovs-ofctl dump-flows br-int table=0 > offlows_table0.txt
ebb439
+AT_CAPTURE_FILE([offlows_table0.txt])
ebb439
+AT_CHECK_UNQUOTED([grep -c "in_port=2" offlows_table0.txt], [1], [dnl
ebb439
+0
ebb439
+])
ebb439
+
ebb439
+check ovn-sbctl set SB_Global . options:northd_internal_version=$northd_version
ebb439
+
ebb439
+# It should claim sw0-p2
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p2 chassis=$ch
ebb439
+
ebb439
+as hv1 ovs-ofctl dump-flows br-int table=0 > offlows_table0.txt
ebb439
+AT_CAPTURE_FILE([offlows_table0.txt])
ebb439
+AT_CHECK_UNQUOTED([grep -c "in_port=2" offlows_table0.txt], [0], [dnl
ebb439
+1
ebb439
+])
ebb439
+
ebb439
+as hv1
ebb439
+ovn_remote=$(ovs-vsctl get open . external_ids:ovn-remote | sed s/\"//g)
ebb439
+ovs-vsctl set open . external_ids:ovn-remote=unix:foo
ebb439
+check ovs-vsctl set interface vif2 external_ids:iface-id=foo
ebb439
+
ebb439
+# ovn-controller is not connected to the SB DB. Even though it
ebb439
+# releases sw0-p2, it will not delete the OF flows.
ebb439
+as hv1 ovs-ofctl dump-flows br-int table=0 > offlows_table0.txt
ebb439
+AT_CAPTURE_FILE([offlows_table0.txt])
ebb439
+AT_CHECK_UNQUOTED([grep -c "in_port=2" offlows_table0.txt], [0], [dnl
ebb439
+1
ebb439
+])
ebb439
+
ebb439
+# Change the version to incorrect one and reconnect to the SB DB.
ebb439
+check ovn-sbctl set SB_Global . options:northd_internal_version=bar
ebb439
+
ebb439
+as hv1
ebb439
+check ovs-vsctl set open . external_ids:ovn-remote=$ovn_remote
ebb439
+
ebb439
+sleep 1
ebb439
+
ebb439
+as hv1 ovs-ofctl dump-flows br-int table=0 > offlows_table0.txt
ebb439
+AT_CAPTURE_FILE([offlows_table0.txt])
ebb439
+AT_CHECK_UNQUOTED([grep -c "in_port=2" offlows_table0.txt], [0], [dnl
ebb439
+1
ebb439
+])
ebb439
+
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p2 chassis=$ch
ebb439
+
ebb439
+# Change the ovn-remote to incorrect and set the correct northd version
ebb439
+# and then change back to the correct ovn-remote
ebb439
+as hv1
ebb439
+check ovs-vsctl set open . external_ids:ovn-remote=unix:foo
ebb439
+
ebb439
+check ovn-sbctl set SB_Global . options:northd_internal_version=$northd_version
ebb439
+
ebb439
+as hv1
ebb439
+check ovs-vsctl set open . external_ids:ovn-remote=$ovn_remote
ebb439
+
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p2 'chassis=[[]]'
ebb439
+as hv1 ovs-ofctl dump-flows br-int table=0 > offlows_table0.txt
ebb439
+AT_CAPTURE_FILE([offlows_table0.txt])
ebb439
+AT_CHECK_UNQUOTED([grep -c "in_port=2" offlows_table0.txt], [1], [dnl
ebb439
+0
ebb439
+])
ebb439
+
ebb439
+as hv1
ebb439
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
ebb439
+
ebb439
+as ovn-sb
ebb439
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
ebb439
+
ebb439
+as ovn-nb
ebb439
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
ebb439
+
ebb439
+AT_CLEANUP
ebb439
-- 
ebb439
2.28.0
ebb439