From patchwork Wed Apr 17 15:55:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Martins X-Patchwork-Id: 1087035 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44kmzS4bDcz9s3l for ; Thu, 18 Apr 2019 01:55:32 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id F170F1064; Wed, 17 Apr 2019 15:55:30 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id A2BB4FD4 for ; Wed, 17 Apr 2019 15:55:29 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 5D2F114D for ; Wed, 17 Apr 2019 15:55:28 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 083D73004144; Wed, 17 Apr 2019 15:55:28 +0000 (UTC) Received: from lucas-t460s.redhat.com (unknown [10.36.118.11]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6A04360C62; Wed, 17 Apr 2019 15:55:26 +0000 (UTC) From: lmartins@redhat.com To: dev@openvswitch.org Date: Wed, 17 Apr 2019 16:55:24 +0100 Message-Id: <20190417155524.17087-1-lmartins@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Wed, 17 Apr 2019 15:55:28 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v5] OVN: Add support for Transport Zones X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Lucas Alvares Gomes 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 Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2019-February/048255.html Signed-off-by: Lucas Alvares Gomes Acked-by: Numan Siddique --- 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 | 151 ++++++++++++++++++++++++++++ 10 files changed, 258 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: cms_option1,cms_option2:foo. + +
external_ids:ovn-transport-zones
+
+ 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. +

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[]) * _ */ 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..e6401c83f 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.2.1", + "cksum": "1575754731 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 ovn-controller(8) for more information. + + ovn-controller populates this key with the transport + zones configured in the column of the Open_vSwitch + database's table. + See ovn-controller(8) for more information. + + The overall purpose of these columns is described under Common Columns at the beginning of this document. diff --git a/tests/ovn.at b/tests/ovn.at index b3500e8c9..ad4d23caa 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -13652,3 +13652,154 @@ 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 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 + +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 + +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