[ovs-dev] [PATCH v6] OVN: Add support for Transport Zones

lmartins at redhat.com lmartins at redhat.com
Thu Apr 18 13:39:09 UTC 2019


From: Lucas Alvares Gomes <lucasagomes at gmail.com>

This patch is adding support for Transport Zones. Transport zones (a.k.a
TZs) is way to enable users of OVN to separate Chassis into different
logical groups that will only form tunnels between members of the same
groups. Each Chassis can belong to one or more Transport Zones. If
not set, the Chassis will be considered part of a default group.

Configuring Transport Zones is done by creating a key called
"ovn-transport-zones" in the external_ids column of the Open_vSwitch
table from the local OVS instance. The value is a string with the name
of the Transport Zone that this instance is part of. Multiple TZs can
be specified with a comma-separated list. For example:

$ sudo ovs-vsctl set open . external-ids:ovn-transport-zones=tz1

or

$ sudo ovs-vsctl set open . external-ids:ovn-transport-zones=tz1,tz2,tz3

This configuration is also exposed in the Chassis table of the OVN
Southbound Database in a new column called "transport_zones".

The use for Transport Zones includes but are not limited to:

* Edge computing: As a way to preventing edge sites from trying to create
  tunnels with every node on every other edge site while still allowing
  these sites to create tunnels with the central node.

* Extra security layer: Where users wants to create "trust zones"
  and prevent computes in a more secure zone to communicate with a less
  secure zone.

This patch is also backward compatible so the upgrade guide for OVN [0]
is still valid and the ovn-controller service can be upgraded before the
OVSDBs.

[0] http://docs.openvswitch.org/en/latest/intro/install/ovn-upgrades/

Reported-by: Daniel Alvarez Sanchez <dalvarez at redhat.com>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2019-February/048255.html
Signed-off-by: Lucas Alvares Gomes <lucasagomes at gmail.com>
---
v5 -> v6
 * Bumped the version of the ovn-sb.ovsschema to 2.3.0.
 * Fixed a synchronization issue with the new test.

v4 -> v5
 * Bumped the version of the ovn-sb.ovsschema to 2.2.1.

v3 -> v4
 * Stopped using the "external_ids" column of the Chassis table and
   instead added a new column called "transport_zones" to hold the set of
   transport zones that the Chassis is part of.

v2 -> v3
 * Enhanced the test to include two more Chassis and assert the case
   where Chassis with no TZs set will have tunnels formed between them.
 * Rebased the patch on top of the latest master branch.

v1 -> v2                                                                       
 * Rename the function check_chassis_tzones to chassis_tzones_overlap.
 * Fix a memory leak in chassis_tzones_overlap.
 * Pass the transport_zones to encaps_run() as a "const char *"
   instead of "struct sbrec_chassis". With this we can also avoid not
   running the function in case the Chassis entry is not yet created.

 NEWS                                |   3 +
 ovn/controller/chassis.c            |  26 ++++-
 ovn/controller/chassis.h            |   4 +-
 ovn/controller/encaps.c             |  35 +++++-
 ovn/controller/encaps.h             |   4 +-
 ovn/controller/ovn-controller.8.xml |   9 ++
 ovn/controller/ovn-controller.c     |  19 +++-
 ovn/ovn-sb.ovsschema                |   9 +-
 ovn/ovn-sb.xml                      |   8 ++
 tests/ovn.at                        | 163 ++++++++++++++++++++++++++++
 10 files changed, 270 insertions(+), 10 deletions(-)

diff --git a/NEWS b/NEWS
index c7440b476..e09f59d64 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,9 @@ Post-v2.11.0
      * Added Policy-based routing(PBR) support to create permit/deny/reroute
        policies on the logical router. New table(Logical_Router_Policy) added in
        OVN-NB schema. New "ovn-nbctl" commands to add/delete/list PBR policies.
+     * Support for Transport Zones, a way to separate chassis into
+       logical groups which results in tunnels only been formed between
+       members of the same transport zone(s).
    - New QoS type "linux-netem" on Linux.
    - Added support for TLS Server Name Indication (SNI).
 
diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
index 58d5d49d5..0f537f1f7 100644
--- a/ovn/controller/chassis.c
+++ b/ovn/controller/chassis.c
@@ -19,6 +19,7 @@
 #include "chassis.h"
 
 #include "lib/smap.h"
+#include "lib/sset.h"
 #include "lib/vswitch-idl.h"
 #include "openvswitch/dynamic-string.h"
 #include "openvswitch/vlog.h"
@@ -73,13 +74,34 @@ get_cms_options(const struct smap *ext_ids)
     return smap_get_def(ext_ids, "ovn-cms-options", "");
 }
 
+static void
+update_chassis_transport_zones(const struct sset *transport_zones,
+                               const struct sbrec_chassis *chassis_rec)
+{
+    struct sset chassis_tzones_set = SSET_INITIALIZER(&chassis_tzones_set);
+    for (int i = 0; i < chassis_rec->n_transport_zones; i++) {
+        sset_add(&chassis_tzones_set, chassis_rec->transport_zones[i]);
+    }
+
+    /* Only update the transport zones if something changed */
+    if (!sset_equals(transport_zones, &chassis_tzones_set)) {
+        const char **ls_arr = sset_array(transport_zones);
+        sbrec_chassis_set_transport_zones(chassis_rec, ls_arr,
+                                          sset_count(transport_zones));
+        free(ls_arr);
+    }
+
+    sset_destroy(&chassis_tzones_set);
+}
+
 /* Returns this chassis's Chassis record, if it is available. */
 const struct sbrec_chassis *
 chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             struct ovsdb_idl_index *sbrec_chassis_by_name,
             const struct ovsrec_open_vswitch_table *ovs_table,
             const char *chassis_id,
-            const struct ovsrec_bridge *br_int)
+            const struct ovsrec_bridge *br_int,
+            const struct sset *transport_zones)
 {
     const struct sbrec_chassis *chassis_rec
         = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
@@ -157,6 +179,8 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             sbrec_chassis_set_hostname(chassis_rec, hostname);
         }
 
+        update_chassis_transport_zones(transport_zones, chassis_rec);
+
         /* Determine new values for Chassis external-ids. */
         const char *chassis_bridge_mappings
             = get_bridge_mappings(&chassis_rec->external_ids);
diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h
index 6b1c357d2..9847e19f6 100644
--- a/ovn/controller/chassis.h
+++ b/ovn/controller/chassis.h
@@ -25,13 +25,15 @@ struct ovsrec_bridge;
 struct ovsrec_open_vswitch_table;
 struct sbrec_chassis;
 struct sbrec_chassis_table;
+struct sset;
 
 void chassis_register_ovs_idl(struct ovsdb_idl *);
 const struct sbrec_chassis *chassis_run(
     struct ovsdb_idl_txn *ovnsb_idl_txn,
     struct ovsdb_idl_index *sbrec_chassis_by_name,
     const struct ovsrec_open_vswitch_table *,
-    const char *chassis_id, const struct ovsrec_bridge *br_int);
+    const char *chassis_id, const struct ovsrec_bridge *br_int,
+    const struct sset *transport_zones);
 bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
                      const struct sbrec_chassis *);
 
diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
index 610b833de..dcf78108d 100644
--- a/ovn/controller/encaps.c
+++ b/ovn/controller/encaps.c
@@ -195,13 +195,36 @@ chassis_tunnel_add(const struct sbrec_chassis *chassis_rec, const struct sbrec_s
     return tuncnt;
 }
 
+/*
+* Returns true if transport_zones and chassis_rec->transport_zones
+* have at least one common transport zone.
+*/
+static bool
+chassis_tzones_overlap(const struct sset *transport_zones,
+                       const struct sbrec_chassis *chassis_rec)
+{
+    /* If neither Chassis belongs to any transport zones, return true to
+     * form a tunnel between them */
+    if (!chassis_rec->n_transport_zones && sset_is_empty(transport_zones)) {
+        return true;
+    }
+
+    for (int i = 0; i < chassis_rec->n_transport_zones; i++) {
+        if (sset_contains(transport_zones, chassis_rec->transport_zones[i])) {
+            return true;
+        }
+    }
+    return false;
+}
+
 void
 encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
            const struct ovsrec_bridge_table *bridge_table,
            const struct ovsrec_bridge *br_int,
            const struct sbrec_chassis_table *chassis_table,
            const char *chassis_id,
-           const struct sbrec_sb_global *sbg)
+           const struct sbrec_sb_global *sbg,
+           const struct sset *transport_zones)
 {
     if (!ovs_idl_txn || !br_int) {
         return;
@@ -251,7 +274,15 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
 
     SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
         if (strcmp(chassis_rec->name, chassis_id)) {
-            /* Create tunnels to the other chassis. */
+            /* Create tunnels to the other Chassis belonging to the
+             * same transport zone */
+            if (!chassis_tzones_overlap(transport_zones, chassis_rec)) {
+                VLOG_DBG("Skipping encap creation for Chassis '%s' because "
+                         "it belongs to different transport zones",
+                         chassis_rec->name);
+                continue;
+            }
+
             if (chassis_tunnel_add(chassis_rec, sbg, &tc) == 0) {
                 VLOG_INFO("Creating encap for '%s' failed", chassis_rec->name);
                 continue;
diff --git a/ovn/controller/encaps.h b/ovn/controller/encaps.h
index 3e0e110ef..7ed3e0939 100644
--- a/ovn/controller/encaps.h
+++ b/ovn/controller/encaps.h
@@ -25,6 +25,7 @@ struct ovsrec_bridge_table;
 struct sbrec_chassis_table;
 struct sbrec_sb_global;
 struct ovsrec_open_vswitch_table;
+struct sset;
 
 void encaps_register_ovs_idl(struct ovsdb_idl *);
 void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
@@ -32,7 +33,8 @@ void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
                 const struct ovsrec_bridge *br_int,
                 const struct sbrec_chassis_table *,
                 const char *chassis_id,
-                const struct sbrec_sb_global *);
+                const struct sbrec_sb_global *,
+                const struct sset *transport_zones);
 
 bool encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
                     const struct ovsrec_bridge *br_int);
diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml
index fd2e10a7a..072ec5820 100644
--- a/ovn/controller/ovn-controller.8.xml
+++ b/ovn/controller/ovn-controller.8.xml
@@ -167,6 +167,15 @@
         specific to this particular chassis. An example would be:
         <code>cms_option1,cms_option2:foo</code>.
       </dd>
+
+      <dt><code>external_ids:ovn-transport-zones</code></dt>
+      <dd>
+        The transport zone(s) that this chassis belongs to. Transport
+        zones is a way to group different chassis so that tunnels are only
+        formed between members of the same group(s). Multiple transport
+        zones may be specified with a comma-separated list. For example:
+        tz1,tz2,tz3.
+      </dd>
     </dl>
 
     <p>
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index cf4907db3..69eeee5dc 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -512,6 +512,14 @@ get_nb_cfg(const struct sbrec_sb_global_table *sb_global_table)
     return sb ? sb->nb_cfg : 0;
 }
 
+static const char *
+get_transport_zones(const struct ovsrec_open_vswitch_table *ovs_table)
+{
+    const struct ovsrec_open_vswitch *cfg
+        = ovsrec_open_vswitch_table_first(ovs_table);
+    return smap_get_def(&cfg->external_ids, "ovn-transport-zones", "");
+}
+
 static void
 ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
 {
@@ -679,6 +687,11 @@ main(int argc, char *argv[])
              * <datapath-tunnel-key>_<port-tunnel-key> */
             struct sset local_lport_ids = SSET_INITIALIZER(&local_lport_ids);
             struct sset active_tunnels = SSET_INITIALIZER(&active_tunnels);
+            /* Contains the transport zones that this Chassis belongs to */
+            struct sset transport_zones = SSET_INITIALIZER(&transport_zones);
+            sset_from_delimited_string(&transport_zones,
+                get_transport_zones(ovsrec_open_vswitch_table_get(
+                                    ovs_idl_loop.idl)), ",");
 
             const struct ovsrec_bridge *br_int
                 = get_br_int(ovs_idl_txn,
@@ -693,12 +706,13 @@ main(int argc, char *argv[])
                 chassis = chassis_run(
                     ovnsb_idl_txn, sbrec_chassis_by_name,
                     ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
-                    chassis_id, br_int);
+                    chassis_id, br_int, &transport_zones);
                 encaps_run(
                     ovs_idl_txn,
                     ovsrec_bridge_table_get(ovs_idl_loop.idl), br_int,
                     sbrec_chassis_table_get(ovnsb_idl_loop.idl), chassis_id,
-                    sbrec_sb_global_first(ovnsb_idl_loop.idl));
+                    sbrec_sb_global_first(ovnsb_idl_loop.idl),
+                    &transport_zones);
 
                 if (ofctrl_is_connected()) {
                     /* Calculate the active tunnels only if have an an active
@@ -848,6 +862,7 @@ main(int argc, char *argv[])
             sset_destroy(&local_lports);
             sset_destroy(&local_lport_ids);
             sset_destroy(&active_tunnels);
+            sset_destroy(&transport_zones);
 
             struct local_datapath *cur_node, *next_node;
             HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema
index d87a02e7b..2b543c6f5 100644
--- a/ovn/ovn-sb.ovsschema
+++ b/ovn/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "2.2.0",
-    "cksum": "2100715070 17222",
+    "version": "2.3.0",
+    "cksum": "3092285199 17409",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -37,7 +37,10 @@
                 "nb_cfg": {"type": {"key": "integer"}},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
-                             "min": 0, "max": "unlimited"}}},
+                             "min": 0, "max": "unlimited"}},
+                "transport_zones" : {"type": {"key": "string",
+                                              "min": 0,
+                                              "max": "unlimited"}}},
             "isRoot": true,
             "indexes": [["name"]]},
         "Encap": {
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 5c4a852a5..e4ac59f1c 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -293,6 +293,14 @@
       See <code>ovn-controller</code>(8) for more information.
     </column>
 
+    <column name="transport_zones">
+      <code>ovn-controller</code> populates this key with the transport
+      zones configured in the <ref table="Open_vSwitch"
+      column="external_ids:ovn-transport-zones"/> column of the Open_vSwitch
+      database's <ref table="Open_vSwitch" db="Open_vSwitch"/> table.
+      See <code>ovn-controller</code>(8) for more information.
+    </column>
+
     <group title="Common Columns">
       The overall purpose of these columns is described under <code>Common
       Columns</code> at the beginning of this document.
diff --git a/tests/ovn.at b/tests/ovn.at
index b3500e8c9..4607febc8 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -13652,3 +13652,166 @@ ovn-nbctl list logical_switch_port
 ovn-nbctl list logical_router_port
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- test transport zones])
+ovn_start
+
+net_add n1
+for i in 1 2 3 4 5; do
+    sim_add hv$i
+    as hv$i
+    ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.$i.1
+done
+
+dnl Wait for the changes to be propagated
+ovn-nbctl --wait=sb --timeout=3 sync
+ovn-nbctl --wait=hv --timeout=3 sync
+
+dnl Assert that each Chassis has a tunnel formed to every other Chassis
+as hv1
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv2-0
+ovn-hv3-0
+ovn-hv4-0
+ovn-hv5-0
+]])
+
+as hv2
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv3-0
+ovn-hv4-0
+ovn-hv5-0
+]])
+
+as hv3
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv2-0
+ovn-hv4-0
+ovn-hv5-0
+]])
+
+as hv4
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv2-0
+ovn-hv3-0
+ovn-hv5-0
+]])
+
+as hv5
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv2-0
+ovn-hv3-0
+ovn-hv4-0
+]])
+
+dnl Let's now add some Chassis to different transport zones
+dnl * hv1: Will be part of two transport zones: tz1 and tz2 so it
+dnl   should have tunnels formed between the other two Chassis (hv2 and hv3)
+dnl
+dnl * hv2: Will be part of one transport zone: tz1. It should have a tunnel
+dnl   to hv1 but not to hv3
+dnl
+dnl * hv3: Will be part of one transport zone: tz2. It should have a tunnel
+dnl   to hv1 but not to hv2
+dnl
+dnl * hv4 and hv5: Will not have any TZ set so they will keep the tunnels
+dnl   between themselves and remove the tunnels to other Chassis which now
+dnl   belongs to some TZs
+dnl
+as hv1
+ovs-vsctl set open . external-ids:ovn-transport-zones=tz1,tz2
+
+as hv2
+ovs-vsctl set open . external-ids:ovn-transport-zones=tz1
+
+as hv3
+ovs-vsctl set open . external-ids:ovn-transport-zones=tz2
+
+dnl Wait for the changes to be propagated
+ovn-nbctl --wait=sb --timeout=3 sync
+ovn-nbctl --wait=hv --timeout=3 sync
+
+as hv1
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv2-0
+ovn-hv3-0
+]])
+
+as hv2
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+]])
+
+as hv3
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+]])
+
+as hv4
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv5-0
+]])
+
+as hv5
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv4-0
+]])
+
+dnl Removing the transport zones should make all Chassis to create
+dnl tunnels between every other Chassis again
+for i in 1 2 3; do
+    as hv$i
+    ovs-vsctl remove open . external-ids ovn-transport-zones
+done
+
+dnl Wait for the changes to be propagated
+ovn-nbctl --wait=sb --timeout=3 sync
+ovn-nbctl --wait=hv --timeout=3 sync
+
+as hv1
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv2-0
+ovn-hv3-0
+ovn-hv4-0
+ovn-hv5-0
+]])
+
+as hv2
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv3-0
+ovn-hv4-0
+ovn-hv5-0
+]])
+
+as hv3
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv2-0
+ovn-hv4-0
+ovn-hv5-0
+]])
+
+as hv4
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv2-0
+ovn-hv3-0
+ovn-hv5-0
+]])
+
+as hv5
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv1-0
+ovn-hv2-0
+ovn-hv3-0
+ovn-hv4-0
+]])
+
+OVN_CLEANUP([hv1], [hv2], [hv3])
+AT_CLEANUP
-- 
2.21.0



More information about the dev mailing list