5f9769
From 3de7959b9018f53abd06320bc7f1a43ec216db7e Mon Sep 17 00:00:00 2001
5f9769
From: Dumitru Ceara <dceara@redhat.com>
5f9769
Date: Wed, 3 Feb 2021 20:36:41 +0100
5f9769
Subject: [PATCH 2/4] binding: Set Port_Binding.up only if supported.
5f9769
5f9769
The supported upgrade procedure is to always upgrade ovn-controllers
5f9769
first and OVN DBs and ovn-northd later.  This leads however to the
5f9769
situation when ovn-controller might try to set the Port_Binding.up field
5f9769
while the Southbound DB isn't yet aware of this field which would
5f9769
trigger transaction failures and control plane/data plane outages.
5f9769
5f9769
To avoid such situations ovn-controller only sets the Port_Binding.up
5f9769
field if it was explicitly set to 'false'.  This ensures that the SB
5f9769
database was already upgraded.
5f9769
5f9769
On the ovn-northd side, as soon as ovn-northd is upgraded it will update
5f9769
all existing Port_Bindings and explicitly set 'Port_Binding.up' to
5f9769
false, implicitly notifying ovn-controller that it is safe to write to
5f9769
the field.
5f9769
5f9769
Reported-by: Numan Siddique <numans@ovn.org>
5f9769
Fixes: 4d3cb42b076b ("binding: Set Logical_Switch_Port.up when all OVS flows are installed.")
5f9769
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
5f9769
Signed-off-by: Numan Siddique <numans@ovn.org>
5f9769
(cherry picked from upstream commit 8b45fc9b2269df68566e47eee9cdd5f043b595d3)
5f9769
5f9769
Change-Id: I6fb8b59419466bbe43f0d993966d8316320c6327
5f9769
---
5f9769
 controller-vtep/binding.c |  6 ++++--
5f9769
 controller/binding.c      | 33 ++++++++++++++++++++++----------
5f9769
 northd/ovn-northd.c       |  8 ++++++++
5f9769
 tests/ovn.at              | 48 ++++++++++++++++++++++++++++++++++++++++++++++-
5f9769
 4 files changed, 82 insertions(+), 13 deletions(-)
5f9769
5f9769
diff --git a/controller-vtep/binding.c b/controller-vtep/binding.c
5f9769
index d28a598..01d5a16 100644
5f9769
--- a/controller-vtep/binding.c
5f9769
+++ b/controller-vtep/binding.c
5f9769
@@ -110,9 +110,11 @@ update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
5f9769
                      chassis_rec->name);
5f9769
         }
5f9769
 
5f9769
-        bool up = true;
5f9769
         sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
5f9769
-        sbrec_port_binding_set_up(port_binding_rec, &up, 1);
5f9769
+        if (port_binding_rec->n_up) {
5f9769
+            bool up = true;
5f9769
+            sbrec_port_binding_set_up(port_binding_rec, &up, 1);
5f9769
+        }
5f9769
     }
5f9769
 }
5f9769
 
5f9769
diff --git a/controller/binding.c b/controller/binding.c
5f9769
index 353debe..efaa109 100644
5f9769
--- a/controller/binding.c
5f9769
+++ b/controller/binding.c
5f9769
@@ -870,6 +870,11 @@ get_lport_type(const struct sbrec_port_binding *pb)
5f9769
  *   container and virtual ports).
5f9769
  * Otherwise request a notification to be sent when the OVS flows
5f9769
  * corresponding to 'pb' have been installed.
5f9769
+ *
5f9769
+ * Note:
5f9769
+ *   Updates (directly or through a notification) the 'pb->up' field only if
5f9769
+ *   it's explicitly set to 'false'.
5f9769
+ *   This is to ensure compatibility with older versions of ovn-northd.
5f9769
  */
5f9769
 static void
5f9769
 claimed_lport_set_up(const struct sbrec_port_binding *pb,
5f9769
@@ -885,7 +890,7 @@ claimed_lport_set_up(const struct sbrec_port_binding *pb,
5f9769
         return;
5f9769
     }
5f9769
 
5f9769
-    if (pb->chassis != chassis_rec) {
5f9769
+    if (pb->chassis != chassis_rec || (pb->n_up && !pb->up[0])) {
5f9769
         binding_iface_bound_add(pb->logical_port);
5f9769
     }
5f9769
 }
5f9769
@@ -973,7 +978,10 @@ release_lport(const struct sbrec_port_binding *pb, bool sb_readonly,
5f9769
         sbrec_port_binding_set_virtual_parent(pb, NULL);
5f9769
     }
5f9769
 
5f9769
-    sbrec_port_binding_set_up(pb, NULL, 0);
5f9769
+    if (pb->n_up) {
5f9769
+        bool up = false;
5f9769
+        sbrec_port_binding_set_up(pb, &up, 1);
5f9769
+    }
5f9769
     update_lport_tracking(pb, tracked_datapaths);
5f9769
     binding_iface_released_add(pb->logical_port);
5f9769
     VLOG_INFO("Releasing lport %s from this chassis.", pb->logical_port);
5f9769
@@ -2503,7 +2511,10 @@ binding_seqno_run(struct shash *local_bindings)
5f9769
                 ovsrec_interface_update_external_ids_delkey(
5f9769
                     lb->iface, OVN_INSTALLED_EXT_ID);
5f9769
             }
5f9769
-            sbrec_port_binding_set_up(lb->pb, NULL, 0);
5f9769
+            if (lb->pb->n_up) {
5f9769
+                bool up = false;
5f9769
+                sbrec_port_binding_set_up(lb->pb, &up, 1);
5f9769
+            }
5f9769
             simap_put(&binding_iface_seqno_map, lb->name, new_seqno);
5f9769
         }
5f9769
         sset_delete(&binding_iface_bound_set, SSET_NODE_FROM_NAME(iface_id));
5f9769
@@ -2536,7 +2547,6 @@ binding_seqno_install(struct shash *local_bindings)
5f9769
 
5f9769
     SIMAP_FOR_EACH_SAFE (node, node_next, &binding_iface_seqno_map) {
5f9769
         struct shash_node *lb_node = shash_find(local_bindings, node->name);
5f9769
-        bool up = true;
5f9769
 
5f9769
         if (!lb_node) {
5f9769
             goto del_seqno;
5f9769
@@ -2554,12 +2564,15 @@ binding_seqno_install(struct shash *local_bindings)
5f9769
         ovsrec_interface_update_external_ids_setkey(lb->iface,
5f9769
                                                     OVN_INSTALLED_EXT_ID,
5f9769
                                                     "true");
5f9769
-        sbrec_port_binding_set_up(lb->pb, &up, 1);
5f9769
-
5f9769
-        struct shash_node *child_node;
5f9769
-        SHASH_FOR_EACH (child_node, &lb->children) {
5f9769
-            struct local_binding *lb_child = child_node->data;
5f9769
-            sbrec_port_binding_set_up(lb_child->pb, &up, 1);
5f9769
+        if (lb->pb->n_up) {
5f9769
+            bool up = true;
5f9769
+
5f9769
+            sbrec_port_binding_set_up(lb->pb, &up, 1);
5f9769
+            struct shash_node *child_node;
5f9769
+            SHASH_FOR_EACH (child_node, &lb->children) {
5f9769
+                struct local_binding *lb_child = child_node->data;
5f9769
+                sbrec_port_binding_set_up(lb_child->pb, &up, 1);
5f9769
+            }
5f9769
         }
5f9769
 
5f9769
 del_seqno:
5f9769
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
5f9769
index 307ee9c..0dc920b 100644
5f9769
--- a/northd/ovn-northd.c
5f9769
+++ b/northd/ovn-northd.c
5f9769
@@ -3329,6 +3329,14 @@ ovn_port_update_sbrec(struct northd_context *ctx,
5f9769
     if (op->tunnel_key != op->sb->tunnel_key) {
5f9769
         sbrec_port_binding_set_tunnel_key(op->sb, op->tunnel_key);
5f9769
     }
5f9769
+
5f9769
+    /* ovn-controller will update 'Port_Binding.up' only if it was explicitly
5f9769
+     * set to 'false'.
5f9769
+     */
5f9769
+    if (!op->sb->n_up) {
5f9769
+        bool up = false;
5f9769
+        sbrec_port_binding_set_up(op->sb, &up, 1);
5f9769
+    }
5f9769
 }
5f9769
 
5f9769
 /* Remove mac_binding entries that refer to logical_ports which are
5f9769
diff --git a/tests/ovn.at b/tests/ovn.at
5f9769
index 1956f5c..2ef056b 100644
5f9769
--- a/tests/ovn.at
5f9769
+++ b/tests/ovn.at
5f9769
@@ -23697,7 +23697,7 @@ check ovn-nbctl ls-add ls
5f9769
 AS_BOX([add OVS port for existing LSP])
5f9769
 check ovn-nbctl lsp-add ls lsp1
5f9769
 check ovn-nbctl --wait=hv sync
5f9769
-check_column "[]" Port_Binding up logical_port=lsp1
5f9769
+check_column "false" Port_Binding up logical_port=lsp1
5f9769
 
5f9769
 check ovs-vsctl add-port br-int lsp1 -- set Interface lsp1 external-ids:iface-id=lsp1
5f9769
 check_column "true" Port_Binding up logical_port=lsp1
5f9769
@@ -23712,5 +23712,51 @@ check_column "true" Port_Binding up logical_port=lsp2
5f9769
 wait_column "true" nb:Logical_Switch_Port up name=lsp2
5f9769
 OVS_WAIT_UNTIL([test `ovs-vsctl get Interface lsp2 external_ids:ovn-installed` = '"true"'])
5f9769
 
5f9769
+AS_BOX([ovn-controller should not reset Port_Binding.up without northd])
5f9769
+# Pause northd and clear the "up" field to simulate older ovn-northd
5f9769
+# versions writing to the Southbound DB.
5f9769
+as northd ovn-appctl -t ovn-northd pause
5f9769
+as northd-backup ovn-appctl -t ovn-northd pause
5f9769
+
5f9769
+as hv1 ovn-appctl -t ovn-controller debug/pause
5f9769
+check ovn-sbctl clear Port_Binding lsp1 up
5f9769
+as hv1 ovn-appctl -t ovn-controller debug/resume
5f9769
+
5f9769
+# Forcefully release the Port_Binding so ovn-controller reclaims it.
5f9769
+# Make sure the Port_Binding.up field is not updated though.
5f9769
+check ovn-sbctl clear Port_Binding lsp1 chassis
5f9769
+hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
5f9769
+wait_column "$hv1_uuid" Port_Binding chassis logical_port=lsp1
5f9769
+check_column "" Port_Binding up logical_port=lsp1
5f9769
+
5f9769
+# Once northd should explicitly set the Port_Binding.up field to 'false' and
5f9769
+# ovn-controller sets it to 'true' as soon as the update is processed.
5f9769
+as northd ovn-appctl -t ovn-northd resume
5f9769
+as northd-backup ovn-appctl -t ovn-northd resume
5f9769
+wait_column "true" Port_Binding up logical_port=lsp1
5f9769
+wait_column "true" nb:Logical_Switch_Port up name=lsp1
5f9769
+
5f9769
+AS_BOX([ovn-controller should reset Port_Binding.up - from NULL])
5f9769
+# If Port_Binding.up is cleared externally, ovn-northd resets it to 'false'
5f9769
+# and ovn-controller finally sets it to 'true' once the update is processed.
5f9769
+as hv1 ovn-appctl -t ovn-controller debug/pause
5f9769
+check ovn-sbctl clear Port_Binding lsp1 up
5f9769
+check ovn-nbctl --wait=sb sync
5f9769
+wait_column "false" nb:Logical_Switch_Port up name=lsp1
5f9769
+as hv1 ovn-appctl -t ovn-controller debug/resume
5f9769
+wait_column "true" Port_Binding up logical_port=lsp1
5f9769
+wait_column "true" nb:Logical_Switch_Port up name=lsp1
5f9769
+
5f9769
+AS_BOX([ovn-controller should reset Port_Binding.up - from false])
5f9769
+# If Port_Binding.up is externally set to 'false', ovn-controller should sets
5f9769
+# it to 'true' once the update is processed.
5f9769
+as hv1 ovn-appctl -t ovn-controller debug/pause
5f9769
+check ovn-sbctl set Port_Binding lsp1 up=false
5f9769
+check ovn-nbctl --wait=sb sync
5f9769
+wait_column "false" nb:Logical_Switch_Port up name=lsp1
5f9769
+as hv1 ovn-appctl -t ovn-controller debug/resume
5f9769
+wait_column "true" Port_Binding up logical_port=lsp1
5f9769
+wait_column "true" nb:Logical_Switch_Port up name=lsp1
5f9769
+
5f9769
 OVN_CLEANUP([hv1])
5f9769
 AT_CLEANUP
5f9769
-- 
5f9769
1.8.3.1
5f9769