From patchwork Tue Sep 28 12:56:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533872 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=J9H2KjQg; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffg2w1rz9sXS for ; Tue, 28 Sep 2021 22:57:15 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id ED517405D3; Tue, 28 Sep 2021 12:57:08 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hL0BuinDiecI; Tue, 28 Sep 2021 12:57:06 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 7493240688; Tue, 28 Sep 2021 12:57:05 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 08450C0028; Tue, 28 Sep 2021 12:57:03 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 62E1DC000D for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 604AA80F05 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp1.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id JwAiwyKLi0Ym for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp1.osuosl.org (Postfix) with ESMTPS id BDCF580EEE for ; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 9954340D72; Tue, 28 Sep 2021 12:56:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833815; bh=LDsCPbC1rkm27ZbmBl0UqXYFaltvAV+SxdJY0bztW94=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=J9H2KjQgunkFDmsyYVe5YUvn/DiEEGaRLLV1FZz6/6wS4u9qpM3oGhYAFb8JpldDq ET5zO3WEz0QkQd/IaMJUOYxZxNrVJ8z+dj6rrJtuO5c3vax5Ko1tjr4Uu8/MU/LQrO RZkc46INYHKguU3BZ75LwPgZuEQVjYkbdI5UyJwM3xhwPsB6RCCeKpP/KamEf0djqQ kUm0rsfq22tTHjYTTagIBuKEYQdgi7f2m3IQuiS5hgE9sU1pbAtOuRmg17Jf46gMCX 7jGdzQDGQmCfrlp9U4qKLnQBWoNA8qiLlkieLt9zBAdhjJZPq9SKevlt0f1674KaS1 7lc6aGehPpRKw== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:42 +0200 Message-Id: <20210928125653.7329-2-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 01/12] ovn-sb: Add requested_chassis column to Port_Binding. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" To allow for ovn-controller to efficiently monitor all Port_Binding records destined to it, we add a new requested_chassis column with weakRef to the Chassis table. The ovn-controller can monitor this column and only process records for its chassis UUID before having claimed the port. northd will fill this column with UUID of Chassis referenced in Logical_Switch_Port options:requested-chassis by name or hostname. In a subsequent update to the controller we will improve the efficiency of the requested-chassis feature by using the new column instead of each chassis performing string comparison on this option for every Port_Binding record processed. Note that the CMS facing Northbound Logical_Switch_Port:options API remains the same. Signed-off-by: Frode Nordahl --- lib/chassis-index.c | 24 +++++++++ lib/chassis-index.h | 3 ++ northd/northd.c | 45 ++++++++++++++-- northd/northd.h | 1 + northd/ovn-northd.c | 9 +++- northd/ovn_northd.dl | 123 ++++++++++++++++++++++++++++++++++++++++--- ovn-sb.ovsschema | 10 ++-- ovn-sb.xml | 21 ++++++++ tests/ovn-northd.at | 45 ++++++++++++++++ 9 files changed, 266 insertions(+), 15 deletions(-) diff --git a/lib/chassis-index.c b/lib/chassis-index.c index 13120fe3e..4b38036cb 100644 --- a/lib/chassis-index.c +++ b/lib/chassis-index.c @@ -22,6 +22,12 @@ chassis_index_create(struct ovsdb_idl *idl) return ovsdb_idl_index_create1(idl, &sbrec_chassis_col_name); } +struct ovsdb_idl_index * +chassis_hostname_index_create(struct ovsdb_idl *idl) +{ + return ovsdb_idl_index_create1(idl, &sbrec_chassis_col_hostname); +} + /* Finds and returns the chassis with the given 'name', or NULL if no such * chassis exists. */ const struct sbrec_chassis * @@ -40,6 +46,24 @@ chassis_lookup_by_name(struct ovsdb_idl_index *sbrec_chassis_by_name, return retval; } +/* Finds and returns the chassis with the given 'hostname', or NULL if no such + * chassis exists. */ +const struct sbrec_chassis * +chassis_lookup_by_hostname(struct ovsdb_idl_index *sbrec_chassis_by_hostname, + const char *hostname) +{ + struct sbrec_chassis *target = sbrec_chassis_index_init_row( + sbrec_chassis_by_hostname); + sbrec_chassis_index_set_hostname(target, hostname); + + struct sbrec_chassis *retval = sbrec_chassis_index_find( + sbrec_chassis_by_hostname, target); + + sbrec_chassis_index_destroy_row(target); + + return retval; +} + struct ovsdb_idl_index * chassis_private_index_create(struct ovsdb_idl *idl) { diff --git a/lib/chassis-index.h b/lib/chassis-index.h index b9b331f34..bc654da13 100644 --- a/lib/chassis-index.h +++ b/lib/chassis-index.h @@ -19,9 +19,12 @@ struct ovsdb_idl; struct ovsdb_idl_index *chassis_index_create(struct ovsdb_idl *); +struct ovsdb_idl_index *chassis_hostname_index_create(struct ovsdb_idl *); const struct sbrec_chassis *chassis_lookup_by_name( struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name); +const struct sbrec_chassis *chassis_lookup_by_hostname( + struct ovsdb_idl_index *sbrec_chassis_by_hostname, const char *hostname); struct ovsdb_idl_index *chassis_private_index_create(struct ovsdb_idl *); diff --git a/northd/northd.c b/northd/northd.c index cf2467fe1..eb0e5d6de 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -2982,6 +2982,7 @@ ovn_update_ipv6_prefix(struct hmap *ports) static void ovn_port_update_sbrec(struct northd_context *ctx, struct ovsdb_idl_index *sbrec_chassis_by_name, + struct ovsdb_idl_index *sbrec_chassis_by_hostname, const struct ovn_port *op, struct hmap *chassis_qdisc_queues, struct sset *active_ha_chassis_grps) @@ -3163,6 +3164,36 @@ ovn_port_update_sbrec(struct northd_context *ctx, * ha_chassis_group cleared in the same transaction. */ sbrec_port_binding_set_ha_chassis_group(op->sb, NULL); } + + const char *requested_chassis; /* May be NULL. */ + bool reset_requested_chassis = false; + requested_chassis = smap_get(&op->nbsp->options, + "requested-chassis"); + if (requested_chassis) { + const struct sbrec_chassis *chassis; /* May be NULL. */ + chassis = chassis_lookup_by_name(sbrec_chassis_by_name, + requested_chassis); + chassis = chassis ? chassis : chassis_lookup_by_hostname( + sbrec_chassis_by_hostname, requested_chassis); + + if (chassis) { + sbrec_port_binding_set_requested_chassis(op->sb, chassis); + } else { + reset_requested_chassis = true; + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT( + 1, 1); + VLOG_WARN_RL( + &rl, + "Unknown chassis '%s' set as " + "options:requested-chassis on LSP '%s'.", + requested_chassis, op->nbsp->name); + } + } else if (op->sb->requested_chassis) { + reset_requested_chassis = true; + } + if (reset_requested_chassis) { + sbrec_port_binding_set_requested_chassis(op->sb, NULL); + } } else { const char *chassis = NULL; if (op->peer && op->peer->od && op->peer->od->nbr) { @@ -3791,6 +3822,7 @@ ovn_port_allocate_key(struct hmap *ports, struct ovn_port *op) static void build_ports(struct northd_context *ctx, struct ovsdb_idl_index *sbrec_chassis_by_name, + struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct hmap *datapaths, struct hmap *ports) { struct ovs_list sb_only, nb_only, both; @@ -3846,6 +3878,7 @@ build_ports(struct northd_context *ctx, tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp); } ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, + sbrec_chassis_by_hostname, op, &chassis_qdisc_queues, &active_ha_chassis_grps); } @@ -3853,7 +3886,8 @@ build_ports(struct northd_context *ctx, /* Add southbound record for each unmatched northbound record. */ LIST_FOR_EACH_SAFE (op, next, list, &nb_only) { op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn); - ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, op, + ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, + sbrec_chassis_by_hostname, op, &chassis_qdisc_queues, &active_ha_chassis_grps); sbrec_port_binding_set_logical_port(op->sb, op->key); @@ -14208,6 +14242,7 @@ build_meter_groups(struct northd_context *ctx, static void ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_index *sbrec_chassis_by_name, + struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct ovsdb_idl_loop *sb_loop, struct hmap *datapaths, struct hmap *ports, struct ovs_list *lr_list, @@ -14309,7 +14344,8 @@ ovnnb_db_run(struct northd_context *ctx, build_datapaths(ctx, datapaths, lr_list); build_ovn_lbs(ctx, datapaths, &lbs); build_lrouter_lbs(datapaths, &lbs); - build_ports(ctx, sbrec_chassis_by_name, datapaths, ports); + build_ports(ctx, sbrec_chassis_by_name, sbrec_chassis_by_hostname, + datapaths, ports); build_ovn_lr_lbs(datapaths, &lbs); build_ovn_lb_svcs(ctx, ports, &lbs); build_ipam(datapaths, ports); @@ -14656,6 +14692,7 @@ ovnsb_db_run(struct northd_context *ctx, void ovn_db_run(struct northd_context *ctx, struct ovsdb_idl_index *sbrec_chassis_by_name, + struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct ovsdb_idl_loop *ovnsb_idl_loop, const char *ovn_internal_version) { @@ -14668,8 +14705,8 @@ ovn_db_run(struct northd_context *ctx, int64_t start_time = time_wall_msec(); stopwatch_start(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec()); - ovnnb_db_run(ctx, sbrec_chassis_by_name, ovnsb_idl_loop, - &datapaths, &ports, &lr_list, start_time, + ovnnb_db_run(ctx, sbrec_chassis_by_name, sbrec_chassis_by_hostname, + ovnsb_idl_loop, &datapaths, &ports, &lr_list, start_time, ovn_internal_version); stopwatch_stop(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec()); stopwatch_start(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec()); diff --git a/northd/northd.h b/northd/northd.h index ffa2bbb4e..d2a931ada 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -34,6 +34,7 @@ struct northd_context { void ovn_db_run(struct northd_context *ctx, struct ovsdb_idl_index *sbrec_chassis_by_name, + struct ovsdb_idl_index *sbrec_chassis_by_hostname, struct ovsdb_idl_loop *ovnsb_idl_loop, const char *ovn_internal_version); diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 39aa96055..9c33378fb 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -716,6 +716,8 @@ main(int argc, char *argv[]) add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_nat_addresses); + add_column_noalert(ovnsb_idl_loop.idl, + &sbrec_port_binding_col_requested_chassis); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_gateway_chassis); @@ -787,6 +789,7 @@ main(int argc, char *argv[]) ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name); + ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_hostname); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps); @@ -895,6 +898,9 @@ main(int argc, char *argv[]) struct ovsdb_idl_index *sbrec_chassis_by_name = chassis_index_create(ovnsb_idl_loop.idl); + struct ovsdb_idl_index *sbrec_chassis_by_hostname + = chassis_hostname_index_create(ovnsb_idl_loop.idl); + struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name = ha_chassis_group_index_create(ovnsb_idl_loop.idl); @@ -975,7 +981,8 @@ main(int argc, char *argv[]) } if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) { - ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop, + ovn_db_run(&ctx, sbrec_chassis_by_name, + sbrec_chassis_by_hostname, &ovnsb_idl_loop, ovn_internal_version); if (ctx.ovnsb_txn) { check_and_add_supported_dhcp_opts_to_sb_db(&ctx); diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index 22913f05a..e379623a6 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -105,6 +105,22 @@ sb::Out_Datapath_Binding(uuid, tunkey, load_balancers, external_ids) :- */ var load_balancers = set_empty(). +function get_requested_chassis(options: Map) : istring = { + var requested_chassis = match(options.get(i"requested-chassis")) { + None -> i"", + Some{requested_chassis} -> requested_chassis, + }; + requested_chassis +} + +relation RequestedChassis( + name: istring, + chassis: uuid, +) +RequestedChassis(name, chassis) :- + sb::Chassis(._uuid = chassis, .name=name). +RequestedChassis(hostname, chassis) :- + sb::Chassis(._uuid = chassis, .hostname=hostname). /* Proxy table for Out_Datapath_Binding: contains all Datapath_Binding fields, * except tunnel id, which is allocated separately (see PortTunKeyAllocation). */ @@ -120,10 +136,95 @@ relation OutProxy_Port_Binding ( tag: Option, mac: Set, nat_addresses: Set, - external_ids: Map + external_ids: Map, + requested_chassis: Option ) -/* Case 1: Create a Port_Binding per logical switch port that is not of type "router" */ +/* Case 1a: Create a Port_Binding per logical switch port that is not of type + * "router" */ +OutProxy_Port_Binding(._uuid = lsp._uuid, + .logical_port = lsp.name, + .__type = lsp.__type, + .gateway_chassis = set_empty(), + .ha_chassis_group = sp.hac_group_uuid, + .options = options, + .datapath = sw._uuid, + .parent_port = lsp.parent_name, + .tag = tag, + .mac = lsp.addresses, + .nat_addresses = set_empty(), + .external_ids = eids, + .requested_chassis = None) :- + sp in &SwitchPort(.lsp = lsp, .sw = sw), + SwitchPortNewDynamicTag(lsp._uuid, opt_tag), + var tag = match (opt_tag) { + None -> lsp.tag, + Some{t} -> Some{t} + }, + lsp.__type != i"router", + var chassis_name_or_hostname = get_requested_chassis(lsp.options), + chassis_name_or_hostname == i"", + var eids = { + var eids = lsp.external_ids; + match (lsp.external_ids.get(i"neutron:port_name")) { + None -> (), + Some{name} -> eids.insert(i"name", name) + }; + eids + }, + var options = { + var options = lsp.options; + if (sw.other_config.get(i"vlan-passthru") == Some{i"true"}) { + options.insert(i"vlan-passthru", i"true") + }; + options + }. + +/* Case 1b: Create a Port_Binding per logical switch port that is not of type + * "router" and has options "requested-chassis" pointing at chassis name or + * hostname. */ +OutProxy_Port_Binding(._uuid = lsp._uuid, + .logical_port = lsp.name, + .__type = lsp.__type, + .gateway_chassis = set_empty(), + .ha_chassis_group = sp.hac_group_uuid, + .options = options, + .datapath = sw._uuid, + .parent_port = lsp.parent_name, + .tag = tag, + .mac = lsp.addresses, + .nat_addresses = set_empty(), + .external_ids = eids, + .requested_chassis = Some{requested_chassis}) :- + sp in &SwitchPort(.lsp = lsp, .sw = sw), + SwitchPortNewDynamicTag(lsp._uuid, opt_tag), + var tag = match (opt_tag) { + None -> lsp.tag, + Some{t} -> Some{t} + }, + lsp.__type != i"router", + var chassis_name_or_hostname = get_requested_chassis(lsp.options), + chassis_name_or_hostname != i"", + RequestedChassis(chassis_name_or_hostname, requested_chassis), + var eids = { + var eids = lsp.external_ids; + match (lsp.external_ids.get(i"neutron:port_name")) { + None -> (), + Some{name} -> eids.insert(i"name", name) + }; + eids + }, + var options = { + var options = lsp.options; + if (sw.other_config.get(i"vlan-passthru") == Some{i"true"}) { + options.insert(i"vlan-passthru", i"true") + }; + options + }. + +/* Case 1c: Create a Port_Binding per logical switch port that is not of type + * "router" and has options "requested-chassis" pointing at non-existent + * chassis name or hostname. */ OutProxy_Port_Binding(._uuid = lsp._uuid, .logical_port = lsp.name, .__type = lsp.__type, @@ -135,7 +236,8 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, .tag = tag, .mac = lsp.addresses, .nat_addresses = set_empty(), - .external_ids = eids) :- + .external_ids = eids, + .requested_chassis = None) :- sp in &SwitchPort(.lsp = lsp, .sw = sw), SwitchPortNewDynamicTag(lsp._uuid, opt_tag), var tag = match (opt_tag) { @@ -143,6 +245,9 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, Some{t} -> Some{t} }, lsp.__type != i"router", + var chassis_name_or_hostname = get_requested_chassis(lsp.options), + chassis_name_or_hostname != i"", + not RequestedChassis(chassis_name_or_hostname, _), var eids = { var eids = lsp.external_ids; match (lsp.external_ids.get(i"neutron:port_name")) { @@ -186,7 +291,8 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, .tag = None, .mac = lsp.addresses, .nat_addresses = nat_addresses, - .external_ids = eids) :- + .external_ids = eids, + .requested_chassis = None) :- SwitchPortLBIPs(.port = &SwitchPort{.lsp = lsp, .sw = sw, .peer = peer}, .lbips = lbips), var eids = { @@ -280,7 +386,8 @@ OutProxy_Port_Binding(._uuid = lrp._uuid, .tag = None, // always empty for router ports .mac = set_singleton(i"${lrp.mac} ${lrp.networks.map(ival).to_vec().join(\" \")}"), .nat_addresses = set_empty(), - .external_ids = lrp.external_ids) :- + .external_ids = lrp.external_ids, + .requested_chassis = None) :- rp in &RouterPort(.lrp = lrp, .router = router, .peer = peer), RouterPortRAOptionsComplete(lrp._uuid, options0), (var __type, var options1) = match (router.options.get(i"chassis")) { @@ -475,7 +582,8 @@ OutProxy_Port_Binding(._uuid = cr_lrp_uuid, .tag = None, //always empty for router ports .mac = set_singleton(i"${lrp.mac} ${lrp.networks.map(ival).to_vec().join(\" \")}"), .nat_addresses = set_empty(), - .external_ids = lrp.external_ids) :- + .external_ids = lrp.external_ids, + .requested_chassis = None) :- DistributedGatewayPort(lrp, lr_uuid, cr_lrp_uuid), DistributedGatewayPortHAChassisGroup(lrp, hacg_uuid), var redirect_type = match (lrp.options.get(i"redirect-type")) { @@ -520,7 +628,8 @@ sb::Out_Port_Binding(._uuid = pbinding._uuid, .mac = pbinding.mac, .nat_addresses = pbinding.nat_addresses, .external_ids = pbinding.external_ids, - .up = Some{up}) :- + .up = Some{up}, + .requested_chassis = pbinding.requested_chassis) :- pbinding in OutProxy_Port_Binding(), PortTunKeyAllocation(pbinding._uuid, tunkey), QueueIDAllocation(pbinding._uuid, qid), diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index e5ab41db9..122614dd5 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.20.0", - "cksum": "605270161 26670", + "version": "20.21.0", + "cksum": "2362446865 26963", "tables": { "SB_Global": { "columns": { @@ -232,7 +232,11 @@ "external_ids": {"type": {"key": "string", "value": "string", "min": 0, - "max": "unlimited"}}}, + "max": "unlimited"}}, + "requested_chassis": {"type": {"key": {"type": "uuid", + "refTable": "Chassis", + "refType": "weak"}, + "min": 0, "max": 1}}}, "indexes": [["datapath", "tunnel_key"], ["logical_port"]], "isRoot": true}, "MAC_Binding": { diff --git a/ovn-sb.xml b/ovn-sb.xml index 2d4d47d10..150051f26 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2983,6 +2983,27 @@ tcp.flags = RST; + + This column exists so that the ovn-controller can effectively monitor + all records destined for it, and is a + supplement to the option. The option is still required so that + the ovn-controller can check the CMS intent when the chassis pointed + to does not currently exist, which for example occurs when the + ovn-controller is stopped without passing the --restart argument. + + This column must be a + record. This is populated by + ovn-northd when the + is defined and contains a string matching the name or hostname of an + existing chassis. + diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 5de554455..258b58e98 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -5223,3 +5223,48 @@ AT_CHECK([grep lr_in_gw_redirect lrflows | grep cr-DR | sed 's/table=../table=?? AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([check options:requested-chassis fills requested_chassis col]) +ovn_start NORTHD_TYPE + +# Add chassis ch1. +check ovn-sbctl chassis-add ch1 geneve 127.0.0.2 +check ovn-sbctl chassis-add ch2 geneve 127.0.0.3 + +wait_row_count Chassis 2 + +ch1_uuid=`ovn-sbctl --bare --columns _uuid find Chassis name="ch1"` +ch2_uuid=`ovn-sbctl --bare --columns _uuid find Chassis name="ch2"` + +check ovn-sbctl set chassis $ch2_uuid hostname=ch2-hostname + +ovn-nbctl ls-add S1 +ovn-nbctl --wait=sb lsp-add S1 S1-vm1 +ovn-nbctl --wait=sb lsp-add S1 S1-vm2 + +wait_row_count Port_Binding 1 logical_port=S1-vm1 requested_chassis!=$ch1_uuid +wait_row_count Port_Binding 1 logical_port=S1-vm2 requested_chassis!=$ch2_uuid + +ovn-nbctl --wait=sb set logical_switch_port S1-vm1 \ + options:requested-chassis=ch1 + +wait_row_count Port_Binding 1 logical_port=S1-vm1 requested_chassis=$ch1_uuid + +ovn-nbctl --wait=sb set logical_switch_port S1-vm2 \ + options:requested-chassis=ch2-hostname + +wait_row_count Port_binding 1 logical-port=S1-vm2 requested_chassis=$ch2_uuid + +ovn-nbctl --wait=sb remove logical_switch_port S1-vm2 \ + options requested-chassis=ch2-hostname + +wait_row_count Port_binding 1 logical-port=S1-vm2 requested_chassis!=$ch2_uuid + +ovn-nbctl --wait=sb set logical_switch_port S1-vm2 \ + options:requested-chassis=ch2 + +wait_row_count Port_binding 1 logical-port=S1-vm2 requested_chassis=$ch2_uuid + +AT_CLEANUP +]) From patchwork Tue Sep 28 12:56:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533870 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=rOkJ8DZV; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffT1Rg9z9sXS for ; Tue, 28 Sep 2021 22:57:05 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 4B00A80F3C; Tue, 28 Sep 2021 12:57:03 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id PwALQ1a6q5Kr; Tue, 28 Sep 2021 12:57:02 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id 8BA6E80EEE; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1D2F8C001D; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id E84C0C000D for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id C83394021F for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xc12qtlRFRY3 for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp2.osuosl.org (Postfix) with ESMTPS id CF31B401EF for ; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 08B5D40D75; Tue, 28 Sep 2021 12:56:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833816; bh=3KGbHByI+nRlbdHO61jdYdI7hIjwRUaEKI554OxW2tU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=rOkJ8DZVbHLlVypzFL6e/s9B1OoMqcJHhw+GH4BATR4bR0popm1z6wz1+N4xPRbwY fkW4O1yE2KBXr7Be2STn2/WtH1tJHnr7/Xqfxf9ij09mv4VEwlT3oorWMDa5ZC61nF +qB+ljHfBKCJu3vBs9CarIVnlWnHxkvrtri97u4KuwlAiTlPEKNDNqSTQb0uDrCtvo HU2k+7urlBHaRi2NzYX60VwjBKxT92daFNKdVYyVjTUivYjAFnmMXK1auTuCjSXurk U3qGdxGXXIc7GOCJ28eWIW5P8ndbAch6ewFjb6Gtfi2lx7iqaJJfpi3zALFHkC544s a9hMgIy29KV+g== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:43 +0200 Message-Id: <20210928125653.7329-3-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 02/12] test: Fix options:requested-chassis with hostname X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" This test currently passes, but is broken in two ways. 1) The `fetch_column` helper should be used to retrieve the value of hostname, not `fetch` wich results in a "fetch: command not found" error which is currently not caught by the test. As a consequence the requested-chassis option was set to the empty string ("") and not the chassis hostname. 2) When we introduced testing with TLS+RBAC in c948d6bb05b4 the ovn_az_attach helper was updated to set the hostname to match system-id. This of course also makes the name and hostname columns in the Chassis record contain the same value which made this test no longer do what it says on the tin. Update test to explicitly set the value to be used for requested-chassis option in the Chassis hostname record, and add a check for it not being empty nor equal to chassis name. Fixes: 4afe409e95c7 ("tests: Introduce new testing helpers.") Fixes: c948d6bb05b4 ("tests: Test with SSL and RBAC for controller by default") Signed-off-by: Frode Nordahl --- tests/ovn.at | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ovn.at b/tests/ovn.at index 172b5c713..13e97cfd2 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -13471,13 +13471,15 @@ ovn-nbctl lsp-add ls0 lsp0 net_add n1 sim_add hv1 as hv1 +ovs-vsctl set Open_vSwitch . external-ids:hostname=hv1-hostname ovs-vsctl add-br br-phys ovn_attach n1 br-phys 192.168.0.11 ovs-vsctl -- add-port br-int hv1-vif0 -- set Interface hv1-vif0 ofport-request=1 wait_row_count Chassis 1 name=hv1 -hv1_hostname=$(fetch Chassis hostname name=hv1) +hv1_hostname=$(fetch_column Chassis hostname name=hv1) echo "hv1_hostname=${hv1_hostname}" +AT_CHECK([test -n "${hv1_hostname}" -a "${hv1_hostname}" != hv1], [1], []) check ovn-nbctl --wait=hv lsp-set-options lsp0 requested-chassis=${hv1_hostname} as hv1 ovs-vsctl set interface hv1-vif0 external-ids:iface-id=lsp0 From patchwork Tue Sep 28 12:56:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533871 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=RZcIrHPJ; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffY3mvcz9sXS for ; Tue, 28 Sep 2021 22:57:09 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id BAB3581751; Tue, 28 Sep 2021 12:57:07 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2kXGGdHkJOgX; Tue, 28 Sep 2021 12:57:06 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id 1ACE380F50; Tue, 28 Sep 2021 12:57:04 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2DA6BC0025; Tue, 28 Sep 2021 12:57:02 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id E9E55C0011 for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id D803080F05 for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id HHd-oisJIo4m for ; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp1.osuosl.org (Postfix) with ESMTPS id B3F3E80EDD for ; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 6316940DD5; Tue, 28 Sep 2021 12:56:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833816; bh=9lqgAmFZqsKw21DcvKE0oDPubzOWutf482atpC/5UCE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RZcIrHPJAoB8Jius3Fi2q1cGIGS/NHnGwl9xd8kxUWqTCRI+q+5WqaBLK8dISCoij M+KKNRU+Vn+OgGw6fjU8mxXlaU+T5iyhhDwxQDQnjXlIXcRGI+vnq3e4Xnzmd4RkIh qb2M/Rl0zhHVZaPdAaDmIKeyVC9Xkhf5rugwZUxlUgIxgrbkSOUhtnoYV7s2c1Mfrw jMMJIzQAN8OpNHseIlUyJpAIvB6w4Nzs3FPzFpLMJSsfcQ1uWe77VoODUb0CYUuWfv L4dt0MBkJtjMm0G1wHqBNJnfI8D6JVA4Czn4ebagnNZ0h63b4M2AerwEyUol0s6pzY ha5B1WpOWvvkg== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:44 +0200 Message-Id: <20210928125653.7329-4-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 03/12] controller: Make use of Port_Binding:requested_chassis X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Improve the efficiency of the requested-chassis feature by using the new Southbound Port_Binding:requested_chassis column instead of each chassis performing string comparison for every Port_Binding record processed. Signed-off-by: Frode Nordahl --- controller/binding.c | 36 +++++++++++++++++++----------------- controller/ovn-controller.c | 3 +++ controller/physical.c | 12 +++++++----- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/controller/binding.c b/controller/binding.c index c037b2352..a2eb86c89 100644 --- a/controller/binding.c +++ b/controller/binding.c @@ -1056,11 +1056,15 @@ is_binding_lport_this_chassis(struct binding_lport *b_lport, static bool can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, - const char *requested_chassis) + const struct sbrec_port_binding *pb) { - return !requested_chassis || !requested_chassis[0] - || !strcmp(requested_chassis, chassis_rec->name) - || !strcmp(requested_chassis, chassis_rec->hostname); + /* We need to check for presence of the requested-chassis option in + * addittion to checking the pb->requested_chassis column because this + * column will be set to NULL whenever the option points to a non-existent + * chassis. As the controller routinely clears its own chassis record this + * might occur more often than one might think. */ + return !smap_get(&pb->options, "requested-chassis") + || chassis_rec == pb->requested_chassis; } /* Returns 'true' if the 'lbinding' has binding lports of type LP_CONTAINER, @@ -1098,7 +1102,7 @@ release_binding_lport(const struct sbrec_chassis *chassis_rec, static bool consider_vif_lport_(const struct sbrec_port_binding *pb, - bool can_bind, const char *vif_chassis, + bool can_bind, struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out, struct binding_lport *b_lport, @@ -1139,7 +1143,10 @@ consider_vif_lport_(const struct sbrec_port_binding *pb, "requested-chassis %s", pb->logical_port, b_ctx_in->chassis_rec->name, - vif_chassis); + pb->requested_chassis ? + pb->requested_chassis->name : "(option points at " + "non-existent " + "chassis)"); } } @@ -1162,9 +1169,7 @@ consider_vif_lport(const struct sbrec_port_binding *pb, struct local_binding *lbinding, struct hmap *qos_map) { - const char *vif_chassis = smap_get(&pb->options, "requested-chassis"); - bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec, - vif_chassis); + bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb); if (!lbinding) { lbinding = local_binding_find(&b_ctx_out->lbinding_data->bindings, @@ -1194,8 +1199,8 @@ consider_vif_lport(const struct sbrec_port_binding *pb, } } - return consider_vif_lport_(pb, can_bind, vif_chassis, b_ctx_in, - b_ctx_out, b_lport, qos_map); + return consider_vif_lport_(pb, can_bind, b_ctx_in, b_ctx_out, + b_lport, qos_map); } static bool @@ -1279,12 +1284,9 @@ consider_container_lport(const struct sbrec_port_binding *pb, } ovs_assert(parent_b_lport && parent_b_lport->pb); - const char *vif_chassis = smap_get(&parent_b_lport->pb->options, - "requested-chassis"); - bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec, - vif_chassis); + bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb); - return consider_vif_lport_(pb, can_bind, vif_chassis, b_ctx_in, b_ctx_out, + return consider_vif_lport_(pb, can_bind, b_ctx_in, b_ctx_out, container_b_lport, qos_map); } @@ -1333,7 +1335,7 @@ consider_virtual_lport(const struct sbrec_port_binding *pb, } } - if (!consider_vif_lport_(pb, true, NULL, b_ctx_in, b_ctx_out, + if (!consider_vif_lport_(pb, true, b_ctx_in, b_ctx_out, virtual_b_lport, qos_map)) { return false; } diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index a719beb0e..b0e4174aa 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -232,6 +232,9 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, sbrec_port_binding_add_clause_chassis(&pb, OVSDB_F_EQ, &chassis->header_.uuid); + sbrec_port_binding_add_clause_requested_chassis( + &pb, OVSDB_F_EQ, &chassis->header_.uuid); + /* Ensure that we find out about l2gateway and l3gateway ports that * should be present on this chassis. Otherwise, we might never find * out about those ports, if their datapaths don't otherwise have a VIF diff --git a/controller/physical.c b/controller/physical.c index 0cfb158c8..780093638 100644 --- a/controller/physical.c +++ b/controller/physical.c @@ -1066,11 +1066,13 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, } else { ofport = local_binding_get_lport_ofport(local_bindings, binding->logical_port); - const char *requested_chassis = smap_get(&binding->options, - "requested-chassis"); - if (ofport && requested_chassis && requested_chassis[0] && - strcmp(requested_chassis, chassis->name) && - strcmp(requested_chassis, chassis->hostname)) { + /* We need to check for presence of the requested-chassis option in + * addittion to checking the pb->requested_chassis column because this + * column will be set to NULL whenever the option points to a + * non-existent chassis. As the controller routinely clears its own + * chassis record this might occur more often than one might think. */ + if (ofport && smap_get(&binding->options, "requested-chassis") + && binding->requested_chassis != chassis) { /* Even though there is an ofport for this port_binding, it is * requested on a different chassis. So ignore this ofport. */ From patchwork Tue Sep 28 12:56:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533873 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=CcJNMz/v; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffk5Lc9z9sXS for ; Tue, 28 Sep 2021 22:57:18 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 99C4740723; Tue, 28 Sep 2021 12:57:10 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Kb6_d5wb-Jjb; Tue, 28 Sep 2021 12:57:08 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 1399D40456; Tue, 28 Sep 2021 12:57:07 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id C34D6C002B; Tue, 28 Sep 2021 12:57:03 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id B866AC000D for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 9B12B401EF for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id A326GRkYERe4 for ; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp2.osuosl.org (Postfix) with ESMTPS id BECD040105 for ; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id BFB1940DD6; Tue, 28 Sep 2021 12:56:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833816; bh=Ge8gZWwck7np+yo+7hQKLNyeNDiptsrz0DU0E1Nb3QM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=CcJNMz/vjqmMoY0RtDpH72WqwjGQRWJj9NBg5Q6wmvYI1jlsf5o1Yi7nnMzDLb+wT Ci+b72YDq+tkyKZA4oqA9v0g5boG30nL/oCrbmEda+yvubIxaD0/5n/AkjTo7xyrOo 4neDWDvFYPLIflCb/6T8Wa0Z1HaGb278PFk2ZNp5eNwYDQDbbnjWQEasVC82sD82sg 9Ew2MmQ5tQPAvLufCoeJTxqWDalIQ/Wrs6XBep0q+wjQ4K0gHqt1kmR5/kN+OBAivN oeKMKo1MvFjLAS9FEqTUG5ij6GICt4DRBpr7ssWPS6xmvnvrcNwXJM14MlGpUJhoFx OxQFRiVL3MnNw== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:45 +0200 Message-Id: <20210928125653.7329-5-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 04/12] controller: Move OVS port functions to new module. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Up until now the controller patch module has been the only consumer of functions to maintain OVS ports and interfaces. With the introduction of infrastructure for plugging providers these functions will also be consumed by the controller binding module. As such we introduce a new module called ovsport where these shared utility functions can live and be consumed by multiple modules. Signed-off-by: Frode Nordahl --- controller/automake.mk | 4 +- controller/ovsport.c | 266 +++++++++++++++++++++++++++++++++++++++++ controller/ovsport.h | 60 ++++++++++ 3 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 controller/ovsport.c create mode 100644 controller/ovsport.h diff --git a/controller/automake.mk b/controller/automake.mk index 41f907d6e..ad2d68af2 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -35,7 +35,9 @@ controller_ovn_controller_SOURCES = \ controller/mac-learn.c \ controller/mac-learn.h \ controller/local_data.c \ - controller/local_data.h + controller/local_data.h \ + controller/ovsport.h \ + controller/ovsport.c controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la man_MANS += controller/ovn-controller.8 diff --git a/controller/ovsport.c b/controller/ovsport.c new file mode 100644 index 000000000..ec38c3fca --- /dev/null +++ b/controller/ovsport.c @@ -0,0 +1,266 @@ +/* Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "ovsport.h" + +#include "lib/vswitch-idl.h" +#include "openvswitch/vlog.h" + +VLOG_DEFINE_THIS_MODULE(ovsport); + +/* Create a port and interface record and add it to 'bridge' in the Open + * vSwitch database represented by 'ovs_idl_txn'. + * + * 'name' is required and is used both for the name of the port and interface + * records. Depending on the contents of the optional 'iface_type' parameter + * the name may need to refer to an existing interface in the system. It is + * the caller's responsibility to ensure that no other port with the desired + * name already exists. + * + * 'iface_type' optionally specifies the type of interface, otherwise set it to + * NULL. + * + * 'port_external_ids' - the contents of the map will be used to fill the + * external_ids column of the created port record, otherwise set it to NULL. + * + * 'iface_external_ids' - the contents of the map will be used to fill the + * external_ids column of the created interface record, otherwise set it to + * NULL. + * + * 'iface_options' - the contents of the map will be used to fill the options + * column of the created interface record, otherwise set it to NULL. + * + * 'iface_mtu_request' - if a value > 0 is provided it will be filled into the + * mtu_request column of the created interface record. */ +void +ovsport_create(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge *bridge, + const char *name, + const char *iface_type, + const struct smap *port_external_ids, + const struct smap *iface_external_ids, + const struct smap *iface_options, + const int64_t iface_mtu_request) +{ + struct ovsrec_interface *iface; + iface = ovsrec_interface_insert(ovs_idl_txn); + ovsrec_interface_set_name(iface, name); + if (iface_type) { + ovsrec_interface_set_type(iface, iface_type); + } + ovsrec_interface_set_external_ids(iface, iface_external_ids); + ovsrec_interface_set_options(iface, iface_options); + ovsrec_interface_set_mtu_request( + iface, &iface_mtu_request, iface_mtu_request > 0); + + struct ovsrec_port *port; + port = ovsrec_port_insert(ovs_idl_txn); + ovsrec_port_set_name(port, name); + ovsrec_port_set_interfaces(port, &iface, 1); + ovsrec_port_set_external_ids(port, port_external_ids); + + ovsrec_bridge_verify_ports(bridge); + ovsrec_bridge_update_ports_addvalue(bridge, port); +} + +/* Remove 'port' from 'bridge' and delete the 'port' record and any records + * with a weakRef to it. */ +void +ovsport_remove(const struct ovsrec_bridge *bridge, + const struct ovsrec_port *port) +{ + ovsrec_bridge_verify_ports(bridge); + ovsrec_port_verify_interfaces(port); + for (struct ovsrec_interface *iface = *port->interfaces; + iface - *port->interfaces < port->n_interfaces; + iface++) { + ovsrec_interface_delete(iface); + } + ovsrec_bridge_update_ports_delvalue(bridge, port); + ovsrec_port_delete(port); +} + +static void update_interface_smap_column( + const struct ovsrec_interface *, const struct smap *, + const struct smap *, void (*fsetkey)(const struct ovsrec_interface *, + const char *, const char *)); +static void maintain_interface_smap_column( + const struct ovsrec_interface *, const struct sset *, + const struct smap *, const struct smap *, + void (*fsetkey)(const struct ovsrec_interface *, const char *, + const char *), + void (*fdelkey)(const struct ovsrec_interface *, + const char *)); + +/* Update interface record as represented by 'iface'. + * + * 'type' optionally specifies the type of interface, to unset type set to an + * empty string, to not update type set to NULL. + * + * 'external_ids' optionally provide a map of external_ids to update, to not + * update external_ids set to NULL. + * + * 'mnt_external_ids' optionally provide set of 'external_ids' to maintain. + * When set the function will make sure that all keys in the 'mnt_external_ids' + * set have values from the 'external_ids' map in the database. Every key that + * exists in 'mnt_external_ids' with no corresponding key in 'external_ids' + * will be removed from the database if present. Set to NULL to not maintain + * the record in this way. + * + * 'options' optionally provide a map of options to update, to not + * update options set to NULL. + * + * 'mnt_options' optionally provide set of 'options' to maintain. + * When set the function will make sure that all keys in the 'mnt_options' set + * have values from the 'options' map in the database. Every key that exists + * in 'mnt_options' with no corresponding key in 'options' will be removed from + * the database if present. Set to NULL to not maintain the record in this + * way. + * + * 'iface_mtu_request' - if a value > 0 is provided it will be filled into the + * mtu_request column of the created interface record. */ +void +ovsport_update_iface(const struct ovsrec_interface *iface, + const char *type, + const struct smap *external_ids, + const struct sset *mnt_external_ids, + const struct smap *options, + const struct sset *mnt_options, + const int64_t mtu_request) +{ + if (type && strcmp(iface->type, type)) { + ovsrec_interface_verify_type(iface); + ovsrec_interface_set_type(iface, type); + } + + if (external_ids && mnt_external_ids) { + ovsrec_interface_verify_external_ids(iface); + maintain_interface_smap_column( + iface, mnt_external_ids, external_ids, &iface->external_ids, + ovsrec_interface_update_external_ids_setkey, + ovsrec_interface_update_external_ids_delkey); + } else if (external_ids) { + ovsrec_interface_verify_external_ids(iface); + update_interface_smap_column( + iface, external_ids, &iface->external_ids, + ovsrec_interface_update_external_ids_setkey); + } + + if (options && mnt_options) { + ovsrec_interface_verify_options(iface); + maintain_interface_smap_column( + iface, mnt_options, options, &iface->options, + ovsrec_interface_update_options_setkey, + ovsrec_interface_update_options_delkey); + } else if (options) { + ovsrec_interface_verify_options(iface); + update_interface_smap_column( + iface, options, &iface->options, + ovsrec_interface_update_options_setkey); + } + + if (mtu_request > 0) { + if ((iface->mtu_request && *iface->mtu_request != mtu_request) + || !iface->mtu_request) + { + ovsrec_interface_verify_mtu_request(iface); + ovsrec_interface_set_mtu_request( + iface, &mtu_request, mtu_request > 0); + } + } else if (iface->mtu_request) { + ovsrec_interface_verify_mtu_request(iface); + ovsrec_interface_update_mtu_request_delvalue(iface, + *iface->mtu_request); + } +} + +const struct ovsrec_port * +ovsport_lookup_by_interfaces( + struct ovsdb_idl_index *ovsrec_port_by_interfaces, + struct ovsrec_interface **interfaces, + const size_t n_interfaces) +{ + struct ovsrec_port *port = ovsrec_port_index_init_row( + ovsrec_port_by_interfaces); + ovsrec_port_index_set_interfaces(port, interfaces, n_interfaces); + + const struct ovsrec_port *retval = ovsrec_port_index_find( + ovsrec_port_by_interfaces, port); + + ovsrec_port_index_destroy_row(port); + + return retval; +} + +const struct +ovsrec_port * ovsport_lookup_by_interface( + struct ovsdb_idl_index *ovsrec_port_by_interfaces, + struct ovsrec_interface *interface) +{ + struct ovsrec_interface *interfaces[] = {interface}; + + return ovsport_lookup_by_interfaces(ovsrec_port_by_interfaces, + interfaces, 1); +} + +/* Update an interface map column with the key/value pairs present in the + * provided smap, only applying changes when necessary. */ +static void +update_interface_smap_column( + const struct ovsrec_interface *iface, + const struct smap *smap, + const struct smap *db_smap, + void (*fsetkey)(const struct ovsrec_interface *, + const char *, const char *)) +{ + struct smap_node *node; + + SMAP_FOR_EACH (node, smap) { + const char *db_value = smap_get(db_smap, node->key); + + if (!db_value || strcmp(db_value, node->value)) { + fsetkey(iface, node->key, node->value); + } + } +} + +/* Like update_interface_smap_column, but also takes an sset with all the keys + * we want to maintain. Any key present in the sset but not in the provided + * smap will be removed from the database if present there. */ +static void +maintain_interface_smap_column( + const struct ovsrec_interface *iface, + const struct sset *mnt_items, + const struct smap *smap, + const struct smap *db_smap, + void (*fsetkey)(const struct ovsrec_interface *, + const char *, const char *), + void (*fdelkey)(const struct ovsrec_interface *, + const char *)) +{ + const char *ref_name; + + SSET_FOR_EACH (ref_name, mnt_items) { + const char *value = smap_get(smap, ref_name); + const char *db_value = smap_get(db_smap, ref_name); + if (!value && db_value) { + fdelkey(iface, ref_name); + } else if (value && (!db_value || strcmp(db_value, value))) + { + fsetkey(iface, ref_name, value); + } + } +} diff --git a/controller/ovsport.h b/controller/ovsport.h new file mode 100644 index 000000000..e355ff7ff --- /dev/null +++ b/controller/ovsport.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVSPORT_H +#define OVSPORT_H 1 + +/* OVS Ports + * ========= + * + * This module contains utility functions for adding, removing and maintaining + * ports and their interface records on OVS bridges. */ + +#include "smap.h" +#include "sset.h" + +#include +#include + +struct ovsdb_idl_txn; +struct ovsrec_bridge; +struct ovsrec_port; +struct ovsrec_interface; +struct ovsdb_idl_index; + +void ovsport_create(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge *bridge, + const char *name, + const char *iface_type, + const struct smap *port_external_ids, + const struct smap *iface_external_ids, + const struct smap *iface_options, + const int64_t iface_mtu_request); +void ovsport_remove(const struct ovsrec_bridge *bridge, + const struct ovsrec_port *port); +void ovsport_update_iface(const struct ovsrec_interface *iface, + const char *type, + const struct smap *external_ids, + const struct sset *mnt_external_ids, + const struct smap *options, + const struct sset *mnt_options, + const int64_t mtu_request); +const struct ovsrec_port * ovsport_lookup_by_interfaces( + struct ovsdb_idl_index *, struct ovsrec_interface **, + const size_t n_interfaces); +const struct ovsrec_port * ovsport_lookup_by_interface( + struct ovsdb_idl_index *, struct ovsrec_interface *); + +#endif /* lib/ovsport.h */ From patchwork Tue Sep 28 12:56:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533877 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=OLWs12Nw; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffp4rF8z9t0k for ; Tue, 28 Sep 2021 22:57:22 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id C820D40722; Tue, 28 Sep 2021 12:57:11 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id jG9CN-uVt1RN; Tue, 28 Sep 2021 12:57:10 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id A2EF0406A6; Tue, 28 Sep 2021 12:57:08 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id B6BEEC0030; Tue, 28 Sep 2021 12:57:04 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 107F7C000D for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id E77B760AF1 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp3.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id GHB6fR16HhA7 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp3.osuosl.org (Postfix) with ESMTPS id 536A360594 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 2644040DD7; Tue, 28 Sep 2021 12:56:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833817; bh=wfhStxVbg/X5SMKKSdhAa+c99+UoZWKNxffSDVizhvk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=OLWs12NwcwKfP+XcEA9hw+vNhqEC3HIgfNt6iy4ZLDak+3oEKryL1lDd+Tl6u3+J6 Blc2K4JQbrHL5JpzXByOjSBzErAqhhmcC98O/sX/x6Dgc0K7IH+K4xb4P/nnAHeZLF iqr+YPD4Hi+t9xs4GkPpPNmPZab37h0YQETq9xVDnpcSklATre/CiAh/svIS1axwzo /kCMMsofJGEc1/2uF5l8r4oUMT2NtH0P/+LAd+Rsui8jOOLOgzNi//1xCiikP1XzPY mJBsQzwP9ODayGoRUxG8NpY4KrqdbI2iS7d/w3EnMgR4K0v/Xk+nItnMOnnEscelWT VDIz0RDpGa4Kw== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:46 +0200 Message-Id: <20210928125653.7329-6-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 05/12] patch: Consume ovsport functions. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Make use of the common functions for maintaining OVS ports. Signed-off-by: Frode Nordahl --- controller/patch.c | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/controller/patch.c b/controller/patch.c index a661025da..0d0d53894 100644 --- a/controller/patch.c +++ b/controller/patch.c @@ -16,6 +16,7 @@ #include #include "patch.h" +#include "ovsport.h" #include "hash.h" #include "lflow.h" @@ -91,28 +92,10 @@ create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn, "ovn-controller: creating patch port '%s' from '%s' to '%s'", src_name, src->name, dst->name); - struct ovsrec_interface *iface; - iface = ovsrec_interface_insert(ovs_idl_txn); - ovsrec_interface_set_name(iface, src_name); - ovsrec_interface_set_type(iface, "patch"); - const struct smap options = SMAP_CONST1(&options, "peer", dst_name); - ovsrec_interface_set_options(iface, &options); - - struct ovsrec_port *port; - port = ovsrec_port_insert(ovs_idl_txn); - ovsrec_port_set_name(port, src_name); - ovsrec_port_set_interfaces(port, &iface, 1); - const struct smap ids = SMAP_CONST1(&ids, key, value); - ovsrec_port_set_external_ids(port, &ids); - - struct ovsrec_port **ports; - ports = xmalloc(sizeof *ports * (src->n_ports + 1)); - memcpy(ports, src->ports, sizeof *ports * src->n_ports); - ports[src->n_ports] = port; - ovsrec_bridge_verify_ports(src); - ovsrec_bridge_set_ports(src, ports, src->n_ports + 1); - - free(ports); + const struct smap if_options = SMAP_CONST1(&if_options, "peer", dst_name); + const struct smap port_ids = SMAP_CONST1(&port_ids, key, value); + ovsport_create(ovs_idl_txn, src, src_name, "patch", &port_ids, NULL, + &if_options, 0); } static void @@ -130,17 +113,7 @@ remove_port(const struct ovsrec_bridge_table *bridge_table, if (bridge->ports[i] != port) { continue; } - struct ovsrec_port **new_ports; - new_ports = xmemdup(bridge->ports, - sizeof *new_ports * (bridge->n_ports - 1)); - if (i != bridge->n_ports - 1) { - /* Removed port was not last */ - new_ports[i] = bridge->ports[bridge->n_ports - 1]; - } - ovsrec_bridge_verify_ports(bridge); - ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1); - free(new_ports); - ovsrec_port_delete(port); + ovsport_remove(bridge, port); return; } } From patchwork Tue Sep 28 12:56:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533874 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=XLESREh5; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffl17B3z9t0T for ; Tue, 28 Sep 2021 22:57:19 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id EBE24823F9; Tue, 28 Sep 2021 12:57:14 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Phv7wMIK-FT3; Tue, 28 Sep 2021 12:57:12 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 31F94818D4; Tue, 28 Sep 2021 12:57:10 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 77D90C0032; Tue, 28 Sep 2021 12:57:05 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 23340C0022 for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id DE08C80EEE for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id fzAJ6cMK6dCa for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp1.osuosl.org (Postfix) with ESMTPS id 3991680EDD for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 829D940DEC; Tue, 28 Sep 2021 12:56:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833817; bh=9I3lCbCR8BmgrAbZg13UxzAQr4QklMHkv7RXa2rPzf4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XLESREh5eSHOdCAEEYybvubY4b0AzIML3s8Rc5e4pHgV4KyllyzzQGFJxjoVwA0uL kTCKtAQubAj42O4DmU2Ww8ZKyF8FqvSbmdmoYQ/1G07R8K++njrt+VMbyyVclZfTho X4uljaAf3RkyxzqcWngQRVkaGRjdPFf1nV+1qiF7lzgPEF6RrtKokD9qNioktkpsR9 SVsQJUmZleaRawnhDce9M56xibfbp28mceejF+kZxi9nASOPFhsj1hUklrep61gtdH 8VcTpiPsIyi2jxagvlioK1dFNheGm2oP1IeLq6z0xRGHRkQ3QsnlmGsqgZQfOcCzoU I4jkjqpat62rw== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:47 +0200 Message-Id: <20210928125653.7329-7-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 06/12] binding: Move can_bind helper to lport module X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" The `can_bind_on_this_chassis` helper is useful outside of the binding module. Since it is related to lports renaming it to `lport_can_bind_on_this_chassis` and putting it into the lport module appears to be appropriate. Signed-off-by: Frode Nordahl --- controller/binding.c | 17 ++--------------- controller/lport.c | 14 ++++++++++++++ controller/lport.h | 3 +++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/controller/binding.c b/controller/binding.c index a2eb86c89..ecfddced5 100644 --- a/controller/binding.c +++ b/controller/binding.c @@ -1054,19 +1054,6 @@ is_binding_lport_this_chassis(struct binding_lport *b_lport, b_lport->pb->chassis == chassis); } -static bool -can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, - const struct sbrec_port_binding *pb) -{ - /* We need to check for presence of the requested-chassis option in - * addittion to checking the pb->requested_chassis column because this - * column will be set to NULL whenever the option points to a non-existent - * chassis. As the controller routinely clears its own chassis record this - * might occur more often than one might think. */ - return !smap_get(&pb->options, "requested-chassis") - || chassis_rec == pb->requested_chassis; -} - /* Returns 'true' if the 'lbinding' has binding lports of type LP_CONTAINER, * 'false' otherwise. */ static bool @@ -1169,7 +1156,7 @@ consider_vif_lport(const struct sbrec_port_binding *pb, struct local_binding *lbinding, struct hmap *qos_map) { - bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb); + bool can_bind = lport_can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb); if (!lbinding) { lbinding = local_binding_find(&b_ctx_out->lbinding_data->bindings, @@ -1284,7 +1271,7 @@ consider_container_lport(const struct sbrec_port_binding *pb, } ovs_assert(parent_b_lport && parent_b_lport->pb); - bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb); + bool can_bind = lport_can_bind_on_this_chassis(b_ctx_in->chassis_rec, pb); return consider_vif_lport_(pb, can_bind, b_ctx_in, b_ctx_out, container_b_lport, qos_map); diff --git a/controller/lport.c b/controller/lport.c index 25b4ef200..f6cab3c7c 100644 --- a/controller/lport.c +++ b/controller/lport.c @@ -108,6 +108,20 @@ lport_get_l3gw_peer(const struct sbrec_port_binding *pb, return get_peer_lport(pb, sbrec_port_binding_by_name); } +bool +lport_can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, + const struct sbrec_port_binding *pb) +{ + /* We need to check for presence of the requested-chassis option in + * addittion to checking the pb->requested_chassis column because this + * column will be set to NULL whenever the option points to a non-existent + * chassis. As the controller routinely clears its own chassis record this + * might occur more often than one might think. */ + return !smap_get(&pb->options, "requested-chassis") + || chassis_rec == pb->requested_chassis; +} + + const struct sbrec_datapath_binding * datapath_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, uint64_t dp_key) diff --git a/controller/lport.h b/controller/lport.h index 43b3d714d..4716c58f9 100644 --- a/controller/lport.h +++ b/controller/lport.h @@ -43,6 +43,9 @@ const struct sbrec_port_binding *lport_lookup_by_key( struct ovsdb_idl_index *sbrec_port_binding_by_key, uint64_t dp_key, uint64_t port_key); +bool lport_can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, + const struct sbrec_port_binding *pb); + const struct sbrec_datapath_binding *datapath_lookup_by_key( struct ovsdb_idl_index *sbrec_datapath_binding_by_key, uint64_t dp_key); From patchwork Tue Sep 28 12:56:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533879 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=XL0ewfyx; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffs5Rytz9sXS for ; Tue, 28 Sep 2021 22:57:25 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 56F838270B; Tue, 28 Sep 2021 12:57:19 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id amVYmn69c5c5; Tue, 28 Sep 2021 12:57:16 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id 5ED17818D4; Tue, 28 Sep 2021 12:57:15 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 6DB08C002A; Tue, 28 Sep 2021 12:57:08 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 393EBC0027 for ; Tue, 28 Sep 2021 12:57:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 07B6D80EDD for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id MHE3bgZ3meGI for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp1.osuosl.org (Postfix) with ESMTPS id 4E66B80F06 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id DF29040DED; Tue, 28 Sep 2021 12:56:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833818; bh=OImPuqS7zBWjSs1zTMK/OtgyFO2OzLGk8XUtxlmvQk0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XL0ewfyxdcNZFXgZbzRhKyjSP52F+IxVpbsaL7ijp297ulxj79jdcWkcVMKi9jlNg CWeejmD+HJc+37DfrRyydTy3Iv6Du1mIi1yZokmJi4kA5eA6C1n5YAghT49KVHmD/F DTxSFndZQIaEkT1HpGRstuiJzloyfrs3cqiA1Sty8pqZoGRjJUWgLL65c1dbrM1Tj/ oU08bbd85xaCD2w7Z+KcO0Ce9ff2rAjOQdIDVrEJsnPDBBdN4DK3pnB70ze8PAY1Ti IaWUnH59AuhJwJldunt/YRXh2s71CDqCeh79TRDfOsXjs+jrF2KZ7VCcPaCd+8NRdG r5GyqY9Khy3iA== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:48 +0200 Message-Id: <20210928125653.7329-8-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 07/12] binding: Make local_binding data structure public X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" The binding module maintains a shash of lports bound to the local chassis. Other modules have interest in the same data for lookup, and it would be wasteful to reimplement the same data structure elsewhere. The incremental processing engine already makes sharing of the data between nodes safe and convenient and this change makes it possible for a different module to consume the data. Signed-off-by: Frode Nordahl --- controller/binding.c | 34 +--------------------------------- controller/binding.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/controller/binding.c b/controller/binding.c index ecfddced5..2fde7629c 100644 --- a/controller/binding.c +++ b/controller/binding.c @@ -507,36 +507,6 @@ update_active_pb_ras_pd(const struct sbrec_port_binding *pb, } } -/* Local bindings. binding.c module binds the logical port (represented by - * Port_Binding rows) and sets the 'chassis' column when it sees the - * OVS interface row (of type "" or "internal") with the - * external_ids:iface-id= set. - * - * This module also manages the other port_bindings. - * - * To better manage the local bindings with the associated OVS interfaces, - * 'struct local_binding' is used. A shash of these local bindings is - * maintained with the 'external_ids:iface-id' as the key to the shash. - * - * struct local_binding has 3 main fields: - * - name : 'external_ids:iface-id' of the OVS interface (key). - * - OVS interface row object. - * - List of 'binding_lport' objects with the primary lport - * in the front of the list (if present). - * - * An object of 'struct local_binding' is created: - * - For each interface that has external_ids:iface-id configured. - * - * - For each port binding (also referred as lport) of type 'LP_VIF' - * if it is a parent lport of container lports even if there is no - * corresponding OVS interface. - */ -struct local_binding { - char *name; - const struct ovsrec_interface *iface; - struct ovs_list binding_lports; -}; - /* This structure represents a logical port (or port binding) * which is associated with 'struct local_binding'. * @@ -559,8 +529,6 @@ static struct local_binding *local_binding_create( const char *name, const struct ovsrec_interface *); static void local_binding_add(struct shash *local_bindings, struct local_binding *); -static struct local_binding *local_binding_find( - const struct shash *local_bindings, const char *name); static void local_binding_destroy(struct local_binding *, struct shash *binding_lports); static void local_binding_delete(struct local_binding *, @@ -2520,7 +2488,7 @@ local_binding_create(const char *name, const struct ovsrec_interface *iface) return lbinding; } -static struct local_binding * +struct local_binding * local_binding_find(const struct shash *local_bindings, const char *name) { return shash_find_data(local_bindings, name); diff --git a/controller/binding.h b/controller/binding.h index 70cc37c78..430a8d9b1 100644 --- a/controller/binding.h +++ b/controller/binding.h @@ -104,11 +104,45 @@ struct binding_ctx_out { struct if_status_mgr *if_mgr; }; +/* Local bindings. binding.c module binds the logical port (represented by + * Port_Binding rows) and sets the 'chassis' column when it sees the + * OVS interface row (of type "" or "internal") with the + * external_ids:iface-id= set. + * + * This module also manages the other port_bindings. + * + * To better manage the local bindings with the associated OVS interfaces, + * 'struct local_binding' is used. A shash of these local bindings is + * maintained with the 'external_ids:iface-id' as the key to the shash. + * + * struct local_binding has 3 main fields: + * - name : 'external_ids:iface-id' of the OVS interface (key). + * - OVS interface row object. + * - List of 'binding_lport' objects with the primary lport + * in the front of the list (if present). + * + * An object of 'struct local_binding' is created: + * - For each interface that has external_ids:iface-id configured. + * + * - For each port binding (also referred as lport) of type 'LP_VIF' + * if it is a parent lport of container lports even if there is no + * corresponding OVS interface. + */ +struct local_binding { + char *name; + const struct ovsrec_interface *iface; + struct ovs_list binding_lports; +}; + + struct local_binding_data { struct shash bindings; struct shash lports; }; +struct local_binding *local_binding_find( + const struct shash *local_bindings, const char *name); + void local_binding_data_init(struct local_binding_data *); void local_binding_data_destroy(struct local_binding_data *); From patchwork Tue Sep 28 12:56:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533880 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=A1wEB1xb; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJfft38dpz9t0T for ; Tue, 28 Sep 2021 22:57:26 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 7D8F2407F1; Tue, 28 Sep 2021 12:57:16 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id tvq_d_EGFlT5; Tue, 28 Sep 2021 12:57:14 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id ED72A40752; Tue, 28 Sep 2021 12:57:11 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 68A3DC0033; Tue, 28 Sep 2021 12:57:06 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2A994C0023 for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 11529403B1 for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EM9XzC2oLyGS for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp4.osuosl.org (Postfix) with ESMTPS id 49ED1403A7 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 42D9040DEE; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833818; bh=v6A/7jmscQGjrr1aNmYjpeJjCacQ3uwe6o9O8fIAj8w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=A1wEB1xbMvbc2OQPmWsZ8KpGJJvJqfQYTNRt79C2bT16GisnVCW1ZDfnnzJov3geI AOaE1uB1pt2GbiywZF3DLbe8sJKrTPY43yo5W57jJ42bopdsQwjpdbWivF0HDyrPAu ZEw3AtqLstWxt80M+E/qPjYgp12zhDXt+/t5zW1l5wBXLmM8sc5dvy1JN7m768reBG RJ+Viih7R9DA6Pa2P8scKumcoYdvsO87sFveHLmOgr7vi47o2/vUL7SFnLXH2jFPwT bTKj6lQotga1dSqGHhWHaPAwu8YCaBBFvZp0XB6klX/xIMlmSFPayIW5zVNKAfsSdI sVW/1BL6YJogQ== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:49 +0200 Message-Id: <20210928125653.7329-9-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 08/12] ovn-controller: Move shared functions to ovn-util X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" The `get_tunnel_type` (and associated enum) and `get_bridge` functions are used in modules other than the `ovn-controller` module. Since the `ovn-controller` module also has the `main` function this organization makes it hard or impossible to unit test. Signed-off-by: Frode Nordahl --- controller/local_data.h | 11 +++-------- controller/ovn-controller.c | 26 -------------------------- controller/ovn-controller.h | 5 ----- lib/ovn-util.c | 27 +++++++++++++++++++++++++++ lib/ovn-util.h | 18 +++++++++++++++++- 5 files changed, 47 insertions(+), 40 deletions(-) diff --git a/controller/local_data.h b/controller/local_data.h index f6317e9ca..c790bc722 100644 --- a/controller/local_data.h +++ b/controller/local_data.h @@ -21,6 +21,9 @@ #include "lib/smap.h" #include "lib/simap.h" +/* OVN includes. */ +#include "lib/ovn-util.h" + struct sbrec_datapath_binding; struct sbrec_port_binding; struct sbrec_chassis; @@ -120,14 +123,6 @@ void tracked_datapath_lport_add(const struct sbrec_port_binding *, struct hmap *tracked_datapaths); void tracked_datapaths_destroy(struct hmap *tracked_datapaths); -/* Must be a bit-field ordered from most-preferred (higher number) to - * least-preferred (lower number). */ -enum chassis_tunnel_type { - GENEVE = 1 << 2, - STT = 1 << 1, - VXLAN = 1 << 0 -}; - /* Maps from a chassis to the OpenFlow port number of the tunnel that can be * used to reach that chassis. */ struct chassis_tunnel { diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index b0e4174aa..8e35188c6 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -140,32 +140,6 @@ struct pending_pkt { /* Registered ofctrl seqno type for nb_cfg propagation. */ static size_t ofctrl_seq_type_nb_cfg; -uint32_t -get_tunnel_type(const char *name) -{ - if (!strcmp(name, "geneve")) { - return GENEVE; - } else if (!strcmp(name, "stt")) { - return STT; - } else if (!strcmp(name, "vxlan")) { - return VXLAN; - } - - return 0; -} - -const struct ovsrec_bridge * -get_bridge(const struct ovsrec_bridge_table *bridge_table, const char *br_name) -{ - const struct ovsrec_bridge *br; - OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) { - if (!strcmp(br->name, br_name)) { - return br; - } - } - return NULL; -} - static unsigned int update_sb_monitors(struct ovsdb_idl *ovnsb_idl, const struct sbrec_chassis *chassis, diff --git a/controller/ovn-controller.h b/controller/ovn-controller.h index 78a53312f..827b7442d 100644 --- a/controller/ovn-controller.h +++ b/controller/ovn-controller.h @@ -40,13 +40,8 @@ struct ct_zone_pending_entry { enum ct_zone_pending_state state; }; -const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *, - const char *br_name); - struct sbrec_encap *preferred_encap(const struct sbrec_chassis *); -uint32_t get_tunnel_type(const char *name); - struct pb_ld_binding { const struct sbrec_port_binding *pb; const struct local_datapath *ld; diff --git a/lib/ovn-util.c b/lib/ovn-util.c index 683ca37d9..19e3c2343 100644 --- a/lib/ovn-util.c +++ b/lib/ovn-util.c @@ -23,6 +23,7 @@ #include "include/ovn/actions.h" #include "openvswitch/ofp-parse.h" #include "openvswitch/vlog.h" +#include "lib/vswitch-idl.h" #include "ovn-dirs.h" #include "ovn-nb-idl.h" #include "ovn-sb-idl.h" @@ -791,3 +792,29 @@ ddlog_err(const char *msg) VLOG_ERR("%s", msg); } #endif + +uint32_t +get_tunnel_type(const char *name) +{ + if (!strcmp(name, "geneve")) { + return GENEVE; + } else if (!strcmp(name, "stt")) { + return STT; + } else if (!strcmp(name, "vxlan")) { + return VXLAN; + } + + return 0; +} + +const struct ovsrec_bridge * +get_bridge(const struct ovsrec_bridge_table *bridge_table, const char *br_name) +{ + const struct ovsrec_bridge *br; + OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) { + if (!strcmp(br->name, br_name)) { + return br; + } + } + return NULL; +} diff --git a/lib/ovn-util.h b/lib/ovn-util.h index b0bc70a16..2fa92e069 100644 --- a/lib/ovn-util.h +++ b/lib/ovn-util.h @@ -285,4 +285,20 @@ void ddlog_warn(const char *msg); void ddlog_err(const char *msg); #endif -#endif + +/* Must be a bit-field ordered from most-preferred (higher number) to + * least-preferred (lower number). */ +enum chassis_tunnel_type { + GENEVE = 1 << 2, + STT = 1 << 1, + VXLAN = 1 << 0 +}; + +uint32_t get_tunnel_type(const char *name); + +struct ovsrec_bridge_table; +const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *, + const char *br_name); + + +#endif /* OVN_UTIL_H */ From patchwork Tue Sep 28 12:56:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533878 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=ZqSf2Agv; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffq5wJcz9t0p for ; Tue, 28 Sep 2021 22:57:23 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 88AD760BB0; Tue, 28 Sep 2021 12:57:20 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aOWuUIjYZJIJ; Tue, 28 Sep 2021 12:57:18 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 1057660B06; Tue, 28 Sep 2021 12:57:17 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id D521DC003F; Tue, 28 Sep 2021 12:57:09 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 55481C0023 for ; Tue, 28 Sep 2021 12:57:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 28FC360594 for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2Dbvp0HmQotk for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp3.osuosl.org (Postfix) with ESMTPS id 82E83607B0 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 9CEF740DEF; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833818; bh=EiWgYaOfjrkdkaD4XxWY8lq8IIi5XBiUHKmF6guL4mg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZqSf2Agvhcph0bKmkVLLKVFoXsfq1+gkEHv9/q8y7jxA/A1oQ06O7WRKrUXF5eZrY Epqz2B0luzEYUinA/6c+TPwQXbvZ/Chse6GPZeEy6M2iAQSCL/d95U7vPeZ0C7O2yf F4V3TSi8EIUvgqQWV8NbId/dN+bp3evCGnA2Iie4GnNAqgZc7J2GczBrevRb/WWFuF ZnIdNyexOipgbGqM/LZrr31hRDgXFbVJef5i2+cYix0DeXzX2G3qhr0Wvxdh/Z1wbe DgSibYjKdhVix0Tz0ONPxLohYs+qtugQk7kJrXtXbtQvy/CuavCpOTZa0J/Xtil7yy NsI7EFsmpyVig== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:50 +0200 Message-Id: <20210928125653.7329-10-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 09/12] tests: Use built objects for unit test deps X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Currently the tests/automake.mk adds source files from the project as needed and rebuilds these for the unit test programs. Use the already built objects instead. Signed-off-by: Frode Nordahl --- tests/automake.mk | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/automake.mk b/tests/automake.mk index 5b890d644..c4a7c0a5b 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -243,17 +243,14 @@ tests_ovstest_SOURCES = \ tests/test-ovn.c \ controller/test-lflow-cache.c \ controller/test-ofctrl-seqno.c \ - controller/lflow-cache.c \ - controller/lflow-cache.h \ - controller/ofctrl-seqno.c \ - controller/ofctrl-seqno.h \ lib/test-ovn-features.c \ - northd/test-ipam.c \ - northd/ipam.c \ - northd/ipam.h + northd/test-ipam.c tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \ - $(OVS_LIBDIR)/libopenvswitch.la lib/libovn.la + $(OVS_LIBDIR)/libopenvswitch.la lib/libovn.la \ + controller/lflow-cache.$(OBJEXT) \ + controller/ofctrl-seqno.$(OBJEXT) \ + northd/ipam.$(OBJEXT) # Python tests. CHECK_PYFILES = \ From patchwork Tue Sep 28 12:56:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533885 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=KGyaofc6; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJfgC72Lkz9sXS for ; Tue, 28 Sep 2021 22:57:43 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 8264480F06; Tue, 28 Sep 2021 12:57:40 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2AiKLlbomltA; Tue, 28 Sep 2021 12:57:34 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 4E9B282423; Tue, 28 Sep 2021 12:57:23 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 44CD6C004A; Tue, 28 Sep 2021 12:57:14 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 211C5C002E for ; Tue, 28 Sep 2021 12:57:04 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id EE4994055F for ; Tue, 28 Sep 2021 12:57:03 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xa1LALSCKPAO for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp2.osuosl.org (Postfix) with ESMTPS id B756940105 for ; Tue, 28 Sep 2021 12:57:00 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 06CFC3F042; Tue, 28 Sep 2021 12:56:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833819; bh=nXBPaVZAebCAtTBIuGFnohMTe+sDNv22Ev6NHWZ2UG0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KGyaofc6Ur/xb7g2FIiDLyVYHqryLmy1iW5rXKVkCxh7uj7fmkuRcmJnyjEU1B34D gkbCzkPG22E/tNG0sN0lqpManmwPMYPIMZA5PJbrswu5exuGsBXQ6gbNxB8rs/WSbP cyhWJWIXUNPbh06MIORaK+p9Ept7Qjs7Mh+YpcKVtKzF5h1Lo/qk6P+bXrdlz5ZSIJ hlBJmwXh+IeacWvLbM7i0mWShvnFe67l7uuSXr6Q/E3jsCGU7UkBawGPHkSFtwIgNE ICS8jt9Il8P+wPYFKAiYRMtzeBwDAI9ZAaURVnt7r4XpSXfhQohNNiJfQnbNrW0shS XYsCG8P0vhD9Q== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:51 +0200 Message-Id: <20210928125653.7329-11-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 10/12] lib: Add infrastructure for plug providers. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" New lib/plug-provider module contains the infrastructure for registering plug provider classes which may be hosted inside or outside the core OVN repository. New controller/plug module adds internal interface for interacting with the plug providers. Extend build system to allow enabling building of built-in plugging providers and linking an externally built plugging provider. Signed-off-by: Frode Nordahl --- Documentation/automake.mk | 2 + Documentation/topics/index.rst | 1 + Documentation/topics/plug_providers/index.rst | 32 + .../topics/plug_providers/plug-providers.rst | 196 ++++++ acinclude.m4 | 49 ++ configure.ac | 2 + controller/automake.mk | 4 +- controller/plug.c | 636 ++++++++++++++++++ controller/plug.h | 80 +++ controller/test-plug.c | 70 ++ lib/automake.mk | 10 +- lib/plug-provider.c | 204 ++++++ lib/plug-provider.h | 164 +++++ lib/plug_providers/dummy/plug-dummy.c | 121 ++++ ovn-architecture.7.xml | 35 +- tests/automake.mk | 13 +- tests/ovn-plug.at | 8 + 17 files changed, 1611 insertions(+), 16 deletions(-) create mode 100644 Documentation/topics/plug_providers/index.rst create mode 100644 Documentation/topics/plug_providers/plug-providers.rst create mode 100644 controller/plug.c create mode 100644 controller/plug.h create mode 100644 controller/test-plug.c create mode 100644 lib/plug-provider.c create mode 100644 lib/plug-provider.h create mode 100644 lib/plug_providers/dummy/plug-dummy.c create mode 100644 tests/ovn-plug.at diff --git a/Documentation/automake.mk b/Documentation/automake.mk index b3fd3d62b..ff245d218 100644 --- a/Documentation/automake.mk +++ b/Documentation/automake.mk @@ -28,6 +28,8 @@ DOC_SOURCE = \ Documentation/topics/ovn-news-2.8.rst \ Documentation/topics/role-based-access-control.rst \ Documentation/topics/debugging-ddlog.rst \ + Documentation/topics/plug_providers/index.rst \ + Documentation/topics/plug_providers/plug-providers.rst \ Documentation/howto/index.rst \ Documentation/howto/docker.rst \ Documentation/howto/firewalld.rst \ diff --git a/Documentation/topics/index.rst b/Documentation/topics/index.rst index d58d5618b..12bd113b7 100644 --- a/Documentation/topics/index.rst +++ b/Documentation/topics/index.rst @@ -41,6 +41,7 @@ OVN high-availability role-based-access-control ovn-news-2.8 + plug_providers/index testing .. list-table:: diff --git a/Documentation/topics/plug_providers/index.rst b/Documentation/topics/plug_providers/index.rst new file mode 100644 index 000000000..837eeae15 --- /dev/null +++ b/Documentation/topics/plug_providers/index.rst @@ -0,0 +1,32 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in OVN documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +============== +Plug Providers +============== + + +.. toctree:: + :maxdepth: 2 + + plug-providers diff --git a/Documentation/topics/plug_providers/plug-providers.rst b/Documentation/topics/plug_providers/plug-providers.rst new file mode 100644 index 000000000..a0a638d1b --- /dev/null +++ b/Documentation/topics/plug_providers/plug-providers.rst @@ -0,0 +1,196 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Convention for heading levels in OVN documentation: + + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + + Avoid deeper levels because they do not render well. + +============== +Plug Providers +============== + +Traditionally it has been the CMSes responsibility to create VIFs as part of +instance life cycle, and subsequently manage plug/unplug operations on the +integration bridge following the conventions described in the +`Open vSwitch Integration Guide`_ for mapping of VIFs to OVN logical port. + +With the advent of NICs connected to multiple distinct CPUs we can have a +topology where the instance runs on one host and Open vSwitch and OVN runs on +a different host, the smartnic control plane CPU. The host facing interfaces +will be visible to Open vSwitch and OVN as representor ports. + +The actions necessary for plugging and unplugging the representor port in +Open vSwitch running on the smartnic control plane CPU would be the same for +every CMS. + +Instead of every CMS having to develop their own version of an agent to do +the plugging, we provide a pluggable infrastructure in OVN that allows the +`ovn-controller` to perform the plugging on CMS direction. + +Hardware or platform specific details for initialization and lookup of +representor ports is provided by an plugging provider library hosted inside or +outside the core OVN repository, and linked at OVN build time. + +Life Cycle of an OVN plugged VIF +-------------------------------- + +1. CMS creates a record in the OVN Northbound Logical_Switch_Port table with + the options column containing the `plug-type` key with a value corresponding + to the `const char *type` provided by the plug provider implementation as + well as a `requested-chassis` key with a value pointing at the name or + hostname of the chassis it wants the VIF plugged on. Additional plug + provider specific key/value pairs must be provided for successful lookup. + +2. `ovn-northd` looks up the name or hostname provided in the + `requested-chassis` option and fills the OVN Southbound Port_Binding + requested_chassis column, it also copies relevant options over to the + Port_Binding record. + +3. `ovn-controller` monitors Southbound Port_Binding entries with a + requested_chassis column pointing at its chassis UUID and when it encounters + a entry with option `plug-type` and it has registered a plug provider + matching that type it will act on it even if no local binding exists yet. + +4. It will fill the `struct plug_port_ctx_in` as defined in `lib/plug.h` with + `op_type` set to 'PLUG_OP_CREATE' and make a call to the plug providers + `plug_port_prepare` callback function. Plug provider performs lookup and + fills the `struct plug_port_ctx_out` as defined in `lib/plug.h`. + +5. `ovn-controller` creates a port and interface record in the local OVSDB + using the details provided by the plug provider and also adds + `external-ids:iface-id` with value matching the logical port name and + `external-ids:ovn-plugged` with value matching the logical port `plug-type`. + When the port creation is done a call will first be made to the plug + providers `plug_port_finish` function and then to the + `plug_port_ctx_destroy` function to free any memory allocated by the plug + implementation. + +6. The Open vSwitch vswitchd will assign a ofport to the newly created + interface and on the next `ovn-controller` incremental engine loop iteration + flows will be installed. + +7. On any change to the Southbound Port_Binding record or full recomputation + the `ovn-controller` will in addition to normal flow processing make a call + to the plug provider again similar to the first creation in case anything + needs updating for the interface record. + +8. The port will be unplugged when an event occurs which would make the + `ovn-controller` release a logical port, for example the Logical_Switch_Port + and Port_Binding entry disappearing from the database or its + `requested_chassis` column being pointed to a different chassis. + + +The plug provider interface +--------------------------- + +The interface between internals of OVN and a plug provider is a set of +callbacks as defined by the `struct plug_class` in `lib/plug-provider.h`. + +It is important to note that these callbacks will be called in the critical +path of the `ovn-controller` processing loop, so care must be taken to make the +implementation as efficient as possible, and under no circumstance can any of +the callback functions make calls that block. + +On `ovn-controller` startup, plug providers made available at build time will +be registered by the identifier provided in the `const char *type` pointer, at +this time the `init` function pointer will be called if it is non-NULL. + +> **Note**: apart from the `const char *type` pointer, no attempt will be made +to access plug provider data or functions before the call to the `init` has +been made. + +On `ovn-controller` exit, the plug providers registered in the above mentioned +procedure will have their `destroy` function pointer called if it is non-NULL. + +If the plug provider has internal lookup tables that need to be maintained they +can define a `run` function which will be called as part of the +`ovn-controller` incremental processing engine loop. If there are any changes +encountered the function should return 'true' to signal that further processing +is necessary, 'false' otherwise. + +On update of Interface records the `ovn-controller` will pass on a `sset` +to the `ovsport_update_iface` function containing options the plug +implementation finds pertinent to maintain for successful operation. This +`sset` is retrieved by making a call to the plug implementation +`plug_get_maintained_iface_options` function pointer if it is non-NULL. This +allows presence of other users of the OVSDB maintaining a different set of +options on the same set of Interface records without wiping out their changes. + +Before creating or updating an existing interface record the plug provider +`plug_port_prepare` function pointer will be called with valid pointers to +`struct plug_port_ctx_in` and `struct plug_port_ctx_out` data structures. If +the plug provider implementation is able to perform lookup it should fill the +`struct plug_port_ctx_out` data structure and return 'true'. The +`ovn-controller` will then create or update the port/interface records and +then call `plug_port_finish` and `plug_port_ctx_destroy`. If the plug provider +implementation is unable to perform lookup or prepare the desired resource at +this time, it should return 'false' which will tell the `ovn-controller` to +signal a full recomputation is necessary, in this case it will also not call +`plug_port_finish`, it will however make a call to `plug_port_ctx_destroy`. + +Before removing port and interface records previously plugged by the +`ovn-controller` as identified by presence of the Interface +`external-ids:ovn-plugged` key, the `ovn-controller` will look up the +`plug-type` from `external-ids:ovn-plugged`, fill `struct plug_port_ctx_in` +with `op_type` set to 'PLUG_OP_REMOVE' and make a call to `plug_port_prepare`. +After the port and interface has been removed a call will be made to +`plug_port_finish`. Both calls will be made with the pointer to +`plug_port_ctx_out` set to 'NULL', and no call will be made to +`plug_port_ctx_destroy`. + +Building with in-tree plug providers +------------------------------------ + +Plug providers hosted in the OVN repository living under `lib/plug_providers`: + +To enable them, provide the `--enable-plug-providers` command line option to +the configure script when building OVN. + +Building with an externally provided plug provider +-------------------------------------------------- + +There is also infrastructure in place to support linking OVN with an externally +built plug provider. + +This external plug provider must define a NULL-terminated array of pointers +to `struct plug_class` data structures named `plug_provider_classes`. Example: + +.. code-block:: C + + const struct plug_class *plug_provider_classes[] = { + &plug_foo, + NULL, + }; + +The name of the repository for the external plug provider should be the same as +the name of the library it produces, and the built library artifact should be +placed in lib/.libs. Example: + +.. code-block:: none + + ovn-vif-foo/ + ovn-vif-foo/lib/.libs/libovn-vif-foo.la + +To enable such a plug provider provide the +`--with-plug-provider=/path/to/ovn-vif-foo` command line option to the +configure script when building OVN. + +.. LINKS +.. _Open vSwitch Integration Guide: + https://docs.openvswitch.org/en/latest/topics/integration/ diff --git a/acinclude.m4 b/acinclude.m4 index e7f829520..793a073d1 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -441,3 +441,52 @@ AC_DEFUN([OVN_CHECK_OVS], [ AC_MSG_CHECKING([OVS version]) AC_MSG_RESULT([$OVSVERSION]) ]) + +dnl OVN_CHECK_PLUG_PROVIDER +dnl +dnl Check for external plug provider +AC_DEFUN([OVN_CHECK_PLUG_PROVIDER], [ + AC_ARG_VAR([PLUG_PROVIDER]) + AC_ARG_WITH( + [plug-provider], + [AC_HELP_STRING([--with-plug-provider=/path/to/provider/repository], + [Specify path to a configured and built plug provider repository])], + [if test "$withval" = yes; then + if test -z "$PLUG_PROVIDER"; then + AC_MSG_ERROR([To build with external plug provider, specify the path to a configured and built plug provider repository --with-plug-provider or in \$PLUG_PROVIDER]), + fi + PLUG_PROVIDER="$(realpath $PLUG_PROVIDER)" + else + PLUG_PROVIDER="$(realpath $withval)" + fi + _plug_provider_name="$(basename $PLUG_PROVIDER)" + if test ! -f "$PLUG_PROVIDER/lib/.libs/lib${_plug_provider_name}.la"; then + AC_MSG_ERROR([$withval is not a configured and built plug provider library repository]) + fi + PLUG_PROVIDER_LDFLAGS="-L$PLUG_PROVIDER/lib/.libs -l$_plug_provider_name" + ], + [PLUG_PROVIDER=no]) + AC_MSG_CHECKING([for plug provider]) + AC_MSG_RESULT([$PLUG_PROVIDER]) + AC_SUBST([PLUG_PROVIDER_LDFLAGS]) + AM_CONDITIONAL([HAVE_PLUG_PROVIDER], [test "$PLUG_PROVIDER" != no]) + if test "$PLUG_PROVIDER" != no; then + AC_DEFINE([HAVE_PLUG_PROVIDER], [1], + [Build and link with external plug provider]) + fi +]) + +dnl OVN_ENABLE_PLUG +dnl +dnl Enable built-in plug providers +AC_DEFUN([OVN_ENABLE_PLUG], [ + AC_ARG_ENABLE( + [plug-providers], + [AC_HELP_STRING([--enable-plug-providers], [Enable building of built-in plug providers])], + [], [enable_plug=no]) + AM_CONDITIONAL([ENABLE_PLUG], [test "$enable_plug" != no]) + if test "$enable_plug" != no; then + AC_DEFINE([ENABLE_PLUG], [1], + [Build built-in plug providers]) + fi +]) diff --git a/configure.ac b/configure.ac index d1b9b4d55..715fe6740 100644 --- a/configure.ac +++ b/configure.ac @@ -172,6 +172,8 @@ OVS_ENABLE_SPARSE OVS_CHECK_DDLOG([0.47]) OVS_CHECK_PRAGMA_MESSAGE OVN_CHECK_OVS +OVN_CHECK_PLUG_PROVIDER +OVN_ENABLE_PLUG OVS_CTAGS_IDENTIFIERS AC_SUBST([OVS_CFLAGS]) AC_SUBST([OVS_LDFLAGS]) diff --git a/controller/automake.mk b/controller/automake.mk index ad2d68af2..09fbbb1af 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -37,7 +37,9 @@ controller_ovn_controller_SOURCES = \ controller/local_data.c \ controller/local_data.h \ controller/ovsport.h \ - controller/ovsport.c + controller/ovsport.c \ + controller/plug.h \ + controller/plug.c controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la man_MANS += controller/ovn-controller.8 diff --git a/controller/plug.c b/controller/plug.c new file mode 100644 index 000000000..cf6f26a98 --- /dev/null +++ b/controller/plug.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* OVS includes */ +#include "lib/vswitch-idl.h" +#include "openvswitch/shash.h" +#include "openvswitch/vlog.h" + +/* OVN includes */ +#include "binding.h" +#include "lib/ovn-sb-idl.h" +#include "lport.h" +#include "ovsport.h" +#include "plug.h" +#include "plug-provider.h" + +VLOG_DEFINE_THIS_MODULE(plug); + +#define OVN_PLUGGED_EXT_ID "ovn-plugged" + +void +plug_register_ovs_idl(struct ovsdb_idl *ovs_idl) +{ + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_mtu_request); +} + +/* Get the class level 'maintained_iface_options' set. */ +const struct sset * +plug_get_maintained_iface_options(const struct plug_class *plug_class) +{ + return plug_class->plug_get_maintained_iface_options(); +} + +/* Prepare the logical port as identified by 'ctx_in' for port creation, update + * or removal as specified by 'ctx_in->op_type'. + * + * When 'ctx_in->op_type' is PLUG_OP_CREATE the plug implementation must fill + * 'ctx_out' with data to apply to the interface record maintained by OVN on + * its behalf. + * + * When 'ctx_in_op_type' is PLUG_OP_REMOVE 'ctx_out' should be set to NULL and + * the plug implementation must not attempt to use 'ctx_out'. + * + * The data in 'ctx_out' is owned by the plug implementation, and a call must + * be made to plug_port_ctx_destroy when done with it. */ +bool +plug_port_prepare(const struct plug_class *plug_class, + const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + if (ctx_out) { + memset(ctx_out, 0, sizeof(*ctx_out)); + } + return plug_class->plug_port_prepare(ctx_in, ctx_out); +} + +/* Notify the plug implementation that a port creation, update or removal has + * been completed */ +void +plug_port_finish(const struct plug_class *plug_class, + const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + plug_class->plug_port_finish(ctx_in, ctx_out); +} + +/* Free any data allocated to 'ctx_out' in a prevous call to + * plug_port_prepare. */ +void +plug_port_ctx_destroy(const struct plug_class *plug_class, + const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + plug_class->plug_port_ctx_destroy(ctx_in, ctx_out); +} + +static struct plug_port_ctx * +build_port_ctx(const struct plug_class *plug, + const enum plug_op_type op_type, + const struct plug_ctx_in *plug_ctx_in, + const struct sbrec_port_binding *pb, + const struct ovsrec_interface *iface, + const char *iface_id) +{ + struct plug_port_ctx *new_ctx = xzalloc( + sizeof *new_ctx); + + new_ctx->plug = plug; + new_ctx->plug_port_ctx_in.op_type = op_type; + new_ctx->plug_port_ctx_in.ovs_table = plug_ctx_in->ovs_table; + new_ctx->plug_port_ctx_in.br_int = plug_ctx_in->br_int; + new_ctx->plug_port_ctx_in.lport_name = pb ? + xstrdup(pb->logical_port) : iface_id ? xstrdup(iface_id) : NULL; + smap_init((struct smap *)&new_ctx->plug_port_ctx_in.lport_options); + smap_init((struct smap *)&new_ctx->plug_port_ctx_in.iface_options); + + if (pb) { + smap_clone((struct smap *)&new_ctx->plug_port_ctx_in.lport_options, + &pb->options); + } + + if (iface) { + new_ctx->plug_port_ctx_in.iface_name = xstrdup(iface->name); + new_ctx->plug_port_ctx_in.iface_type = xstrdup(iface->type); + smap_clone((struct smap *)&new_ctx->plug_port_ctx_in.iface_options, + &iface->options); + } + + return new_ctx; +} + +static void +destroy_port_ctx(struct plug_port_ctx *ctx) +{ + smap_destroy((struct smap *)&ctx->plug_port_ctx_in.lport_options); + smap_destroy((struct smap *)&ctx->plug_port_ctx_in.iface_options); + if (ctx->plug_port_ctx_in.lport_name) { + free((char *)ctx->plug_port_ctx_in.lport_name); + } + if (ctx->plug_port_ctx_in.iface_name) { + free((char *)ctx->plug_port_ctx_in.iface_name); + } + if (ctx->plug_port_ctx_in.iface_type) { + free((char *)ctx->plug_port_ctx_in.iface_type); + } + free(ctx); +} + +/* When we add deletion of rows to the transaction, the data structures + * associated with the rows will immediately be freed from the IDL, and as + * such we can no longer access them. + * + * Since IDL commits are handled asynchronously we can have a few engine + * iterations where the deleted data shows up when iterating over table + * contents, but the IDL *_is_deleted() call will not reliably categorize the + * data as deleted. This is in contrast to the IDL behaviour when some other + * process deletes data from the database, so this may be an OVS IDL bug, or it + * could be it's just expected that the program consuming the IDL will know not + * to access rows it has deleted. + * + * To deal with this, we keep a reference for ourself to avoid attempting to + * remove the same data multiple times while waiting for the transaction to + * commit. The tracking data will be cleared upon successful commit at the + * end of the ovn-controller main loop. + */ +static void +transact_delete_port(const struct plug_ctx_in *plug_ctx_in, + const struct plug_ctx_out *plug_ctx_out, + const struct plug_port_ctx *plug_port_ctx, + const struct ovsrec_port *port) +{ + shash_add(plug_ctx_out->deleted_iface_ids, + plug_port_ctx->plug_port_ctx_in.lport_name, + plug_port_ctx); + ovsport_remove(plug_ctx_in->br_int, port); +} + +static void +transact_create_port(const struct plug_ctx_in *plug_ctx_in, + const struct plug_ctx_out *plug_ctx_out, + const struct plug_port_ctx *plug_port_ctx, + const struct smap *iface_external_ids, + const int64_t mtu_request) +{ + shash_add(plug_ctx_out->changed_iface_ids, + plug_port_ctx->plug_port_ctx_in.lport_name, + plug_port_ctx); + ovsport_create(plug_ctx_in->ovs_idl_txn, plug_ctx_in->br_int, + plug_port_ctx->plug_port_ctx_out.name, + plug_port_ctx->plug_port_ctx_out.type, + NULL, iface_external_ids, + plug_port_ctx->plug_port_ctx_out.iface_options, + mtu_request); +} + +static void +transact_update_port(const struct ovsrec_interface *iface_rec, + const struct plug_ctx_in *plug_ctx_in OVS_UNUSED, + const struct plug_ctx_out *plug_ctx_out, + const struct plug_port_ctx *plug_port_ctx, + const struct smap *iface_external_ids, + const int64_t mtu_request) +{ + shash_add(plug_ctx_out->changed_iface_ids, + plug_port_ctx->plug_port_ctx_in.lport_name, + plug_port_ctx); + ovsport_update_iface(iface_rec, + plug_port_ctx->plug_port_ctx_out.type, + iface_external_ids, + NULL, + plug_port_ctx->plug_port_ctx_out.iface_options, + plug_get_maintained_iface_options( + plug_port_ctx->plug), + mtu_request); +} + + +static bool +consider_unplug_lport(const struct ovsrec_interface *iface, + const struct sbrec_port_binding *pb, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + const char *plug_type = NULL; + plug_type = smap_get(&iface->external_ids, + OVN_PLUGGED_EXT_ID); + + if (plug_type) { + const char *iface_id = smap_get( + &iface->external_ids, "iface-id"); + const struct ovsrec_port *port = ovsport_lookup_by_interface( + plug_ctx_in->ovsrec_port_by_interfaces, + (struct ovsrec_interface *) iface); + if (port) { + const struct plug_class *plug; + if (!(plug = plug_provider_get(plug_type))) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, + "Unable to open plug provider for " + "plug-type: '%s' lport %s", + plug_type, pb->logical_port); + /* While we are unable to handle this, asking for a recompute + * will not change that fact. */ + return true; + } + if (!plug_ctx_in->chassis_rec || !plug_ctx_in->br_int + || !plug_ctx_in->ovs_idl_txn) + { + /* Some of our prerequisites are not available, ask for a + * recompute. */ + return false; + } + + /* Our contract with the plug provider is that plug_port_finish + * will be called with a plug_port_ctx_in object once the data + * is actually deleted. + * + * Since this happens asynchronously we need to allocate memory for + * and duplicate any database references so that they stay valid. + * + * The data is freed with a call to destroy_port_ctx after the + * transaction completes at the end of the ovn-controller main + * loop. */ + struct plug_port_ctx *plug_port_ctx = build_port_ctx( + plug, PLUG_OP_REMOVE, + plug_ctx_in, pb, iface, iface_id); + + if (!plug_port_prepare(plug, &plug_port_ctx->plug_port_ctx_in, + NULL)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_INFO_RL(&rl, + "Not unplugging iface %s (lport %s) on direction " + "from plugging library.", + iface->name, pb ? pb->logical_port : "(none)"); + destroy_port_ctx(plug_port_ctx); + return false; + } + VLOG_INFO("Unplugging port %s from %s for lport %s on this " + "chassis.", + port->name, + plug_ctx_in->br_int->name, + pb ? pb->logical_port : "(none)"); + + /* Add and track delete operation to the transaction */ + transact_delete_port(plug_ctx_in, plug_ctx_out, + plug_port_ctx, port); + return true; + } + } + return true; +} + +static int64_t +get_plug_mtu_request(const struct smap *lport_options) +{ + return smap_get_int(lport_options, "plug-mtu-request", 0); +} + +static bool +consider_plug_lport_create__(const struct plug_class *plug, + const struct smap *iface_external_ids, + const struct sbrec_port_binding *pb, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + if (!plug_ctx_in->chassis_rec || !plug_ctx_in->br_int + || !plug_ctx_in->ovs_idl_txn) { + /* Some of our prerequisites are not available, ask for a recompute. */ + return false; + } + + /* Our contract with the plug provider is that plug_port_finish + * will be called with plug_port_ctx_in and plug_port_ctx_out objects + * once the port is actually created. + * + * Since this happens asynchronously we need to allocate memory for + * and duplicate any database references so that they stay valid. + * + * The data is freed with a call to destroy_port_ctx after the + * transaction completes at the end of the ovn-controller main + * loop. */ + struct plug_port_ctx *plug_port_ctx = build_port_ctx( + plug, PLUG_OP_CREATE, plug_ctx_in, pb, NULL, NULL); + + if (!plug_port_prepare(plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_INFO_RL(&rl, + "Not plugging lport %s on direction from plugging " + "library.", + pb->logical_port); + plug_port_ctx_destroy(plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out); + destroy_port_ctx(plug_port_ctx); + return false; + } + + VLOG_INFO("Plugging port %s into %s for lport %s on this " + "chassis.", + plug_port_ctx->plug_port_ctx_out.name, plug_ctx_in->br_int->name, + pb->logical_port); + transact_create_port(plug_ctx_in, plug_ctx_out, + plug_port_ctx, + iface_external_ids, + get_plug_mtu_request(&pb->options)); + return true; +} + +static bool +consider_plug_lport_update__(const struct plug_class *plug, + const struct smap *iface_external_ids, + const struct sbrec_port_binding *pb, + struct local_binding *lbinding, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + if (!plug_ctx_in->chassis_rec || !plug_ctx_in->br_int + || !plug_ctx_in->ovs_idl_txn) { + /* Some of our prerequisites are not available, ask for a recompute. */ + return false; + } + /* Our contract with the plug provider is that plug_port_finish + * will be called with plug_port_ctx_in and plug_port_ctx_out objects + * once the port is actually updated. + * + * Since this happens asynchronously we need to allocate memory for + * and duplicate any database references so that they stay valid. + * + * The data is freed with a call to destroy_port_ctx after the + * transaction completes at the end of the ovn-controller main + * loop. */ + struct plug_port_ctx *plug_port_ctx = build_port_ctx( + plug, PLUG_OP_CREATE, plug_ctx_in, pb, NULL, NULL); + + if (!plug_port_prepare(plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_INFO_RL(&rl, + "Not updating lport %s on direction from plugging " + "library.", + pb->logical_port); + plug_port_ctx_destroy(plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out); + destroy_port_ctx(plug_port_ctx); + return false; + } + + if (strcmp(lbinding->iface->name, plug_port_ctx->plug_port_ctx_out.name)) { + VLOG_WARN("Attempt of incompatible change to existing " + "port detected, please recreate port: %s", + pb->logical_port); + plug_port_ctx_destroy(plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out); + destroy_port_ctx(plug_port_ctx); + return false; + } + VLOG_DBG("updating iface for: %s", pb->logical_port); + transact_update_port(lbinding->iface, plug_ctx_in, plug_ctx_out, + plug_port_ctx, iface_external_ids, + get_plug_mtu_request(&pb->options)); + + return true; +} + +static bool +consider_plug_lport(const struct sbrec_port_binding *pb, + struct local_binding *lbinding, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + bool ret = true; + if (lport_can_bind_on_this_chassis(plug_ctx_in->chassis_rec, pb)) { + const char *plug_type = smap_get(&pb->options, "plug-type"); + if (!plug_type) { + /* Nothing for us to do and we don't need a recompute. */ + return true; + } + + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + const struct plug_class *plug; + if (!(plug = plug_provider_get(plug_type))) { + VLOG_WARN_RL(&rl, + "Unable to open plug provider for plug-type: '%s' " + "lport %s", + plug_type, pb->logical_port); + /* While we are unable to handle this, asking for a recompute will + * not change that fact. */ + return true; + } + const struct smap iface_external_ids = SMAP_CONST2( + &iface_external_ids, + OVN_PLUGGED_EXT_ID, plug_type, + "iface-id", pb->logical_port); + if (lbinding && lbinding->iface) { + if (!smap_get(&lbinding->iface->external_ids, + OVN_PLUGGED_EXT_ID)) + { + VLOG_WARN_RL(&rl, + "CMS requested plugging of lport %s, but a port " + "that is not maintained by OVN already exsist " + "in local vSwitch: "UUID_FMT, + pb->logical_port, + UUID_ARGS(&lbinding->iface->header_.uuid)); + return false; + } + ret = consider_plug_lport_update__(plug, &iface_external_ids, pb, + lbinding, plug_ctx_in, + plug_ctx_out); + } else { + ret = consider_plug_lport_create__(plug, &iface_external_ids, pb, + plug_ctx_in, plug_ctx_out); + } + } + + return ret; +} + +static bool +plug_is_deleted_iface_id(const struct plug_ctx_out *plug_ctx_out, + const char *iface_id) +{ + return shash_find(plug_ctx_out->deleted_iface_ids, iface_id) != NULL; +} + +static bool +plug_handle_lport_vif(const struct sbrec_port_binding *pb, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + if (plug_is_deleted_iface_id(plug_ctx_out, pb->logical_port)) { + return true; + } + bool handled = true; + struct local_binding *lbinding = local_binding_find( + plug_ctx_in->local_bindings, pb->logical_port); + + if (lport_can_bind_on_this_chassis(plug_ctx_in->chassis_rec, pb)) { + handled &= consider_plug_lport(pb, lbinding, + plug_ctx_in, plug_ctx_out); + } else if (lbinding && lbinding->iface + && !shash_find( + plug_ctx_out->deleted_iface_ids, pb->logical_port)) { + handled &= consider_unplug_lport(lbinding->iface, pb, + plug_ctx_in, plug_ctx_out); + } + return handled; +} + +static bool +plug_handle_iface(const struct ovsrec_interface *iface_rec, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + bool handled = true; + const char *plug_type = smap_get(&iface_rec->external_ids, + OVN_PLUGGED_EXT_ID); + const char *iface_id = smap_get(&iface_rec->external_ids, "iface-id"); + if (!plug_type || !iface_id + || plug_is_deleted_iface_id(plug_ctx_out, iface_id)) { + return true; + } + struct local_binding *lbinding = local_binding_find( + plug_ctx_in->local_bindings, iface_id); + const struct sbrec_port_binding *pb = lport_lookup_by_name( + plug_ctx_in->sbrec_port_binding_by_name, iface_id); + if (pb && lbinding + && lport_can_bind_on_this_chassis(plug_ctx_in->chassis_rec, pb)) { + /* Something changed on a interface we have previously plugged, + * consider updating it */ + handled &= consider_plug_lport(pb, lbinding, + plug_ctx_in, plug_ctx_out); + } else if (!pb + || !lport_can_bind_on_this_chassis( + plug_ctx_in->chassis_rec, pb)) { + /* No lport for this interface or it is destined for different chassis, + * consuder unplugging it */ + handled &= consider_unplug_lport(iface_rec, pb, + plug_ctx_in, plug_ctx_out); + } + return handled; +} + +void +plug_run(struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + const struct sbrec_port_binding *pb; + SBREC_PORT_BINDING_TABLE_FOR_EACH (pb, + plug_ctx_in->port_binding_table) { + if (sbrec_port_binding_is_deleted(pb)) { + continue; + } + enum en_lport_type lport_type = get_lport_type(pb); + if (lport_type == LP_VIF) { + plug_handle_lport_vif(pb, plug_ctx_in, plug_ctx_out); + } + } + const struct ovsrec_interface *iface_rec; + OVSREC_INTERFACE_TABLE_FOR_EACH (iface_rec, + plug_ctx_in->iface_table) { + if (ovsrec_interface_is_deleted(iface_rec)) { + continue; + } + plug_handle_iface(iface_rec, plug_ctx_in, plug_ctx_out); + } +} + +bool +plug_handle_port_binding_changes(struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + const struct sbrec_port_binding *pb; + bool handled = true; + + /* handle deleted lports */ + SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED ( + pb, + plug_ctx_in->port_binding_table) { + if (!sbrec_port_binding_is_deleted(pb)) { + continue; + } + + enum en_lport_type lport_type = get_lport_type(pb); + if (lport_type == LP_VIF) { + struct local_binding *lbinding = local_binding_find( + plug_ctx_in->local_bindings, pb->logical_port); + if (lbinding && lbinding->iface + && !plug_is_deleted_iface_id(plug_ctx_out, pb->logical_port)) { + handled &= consider_unplug_lport(lbinding->iface, pb, + plug_ctx_in, plug_ctx_out); + } + } + } + + /* handle any new or updated lports */ + SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED ( + pb, + plug_ctx_in->port_binding_table) { + if (sbrec_port_binding_is_deleted(pb)) { + continue; + } + enum en_lport_type lport_type = get_lport_type(pb); + if (lport_type == LP_VIF) { + handled &= plug_handle_lport_vif(pb, plug_ctx_in, plug_ctx_out); + } + } + return handled; +} + +bool +plug_handle_ovs_interface_changes(struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + bool handled = true; + const struct ovsrec_interface *iface_rec; + + OVSREC_INTERFACE_TABLE_FOR_EACH_TRACKED (iface_rec, + plug_ctx_in->iface_table) { + if (ovsrec_interface_is_deleted(iface_rec)) { + continue; + } + handled &= plug_handle_iface(iface_rec, plug_ctx_in, plug_ctx_out); + } + return handled; +} + +void +plug_finish_deleted(struct shash *deleted_iface_ids) +{ + struct shash_node *node, *next; + SHASH_FOR_EACH_SAFE (node, next, deleted_iface_ids) { + struct plug_port_ctx *plug_port_ctx = node->data; + plug_port_finish(plug_port_ctx->plug, + &plug_port_ctx->plug_port_ctx_in, + NULL); + shash_delete(deleted_iface_ids, node); + destroy_port_ctx(plug_port_ctx); + } +} + +void +plug_finish_changed(struct shash *changed_iface_ids) +{ + struct shash_node *node, *next; + SHASH_FOR_EACH_SAFE (node, next, changed_iface_ids) { + struct plug_port_ctx *plug_port_ctx = node->data; + plug_port_finish(plug_port_ctx->plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out); + plug_port_ctx_destroy(plug_port_ctx->plug, + &plug_port_ctx->plug_port_ctx_in, + &plug_port_ctx->plug_port_ctx_out); + shash_delete(changed_iface_ids, node); + destroy_port_ctx(plug_port_ctx); + } +} diff --git a/controller/plug.h b/controller/plug.h new file mode 100644 index 000000000..d18e1c522 --- /dev/null +++ b/controller/plug.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLUG_H +#define PLUG_H 1 + +/* + * Plug, the controller internal interface to the plug provider infrastructure. + */ + +#include "openvswitch/shash.h" +#include "smap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct plug_ctx_in { + struct ovsdb_idl_txn *ovs_idl_txn; + struct ovsdb_idl_index *sbrec_port_binding_by_name; + struct ovsdb_idl_index *ovsrec_port_by_interfaces; + const struct ovsrec_open_vswitch_table *ovs_table; + const struct ovsrec_bridge *br_int; + const struct ovsrec_interface_table *iface_table; + const struct sbrec_chassis *chassis_rec; + const struct sbrec_port_binding_table *port_binding_table; + const struct shash *local_bindings; +}; + +struct plug_ctx_out { + struct shash *deleted_iface_ids; + struct shash *changed_iface_ids; +}; + +struct plug_class; +struct plug_port_ctx_out; +struct plug_port_ctx_in; + +const struct sset * plug_get_maintained_iface_options( + const struct plug_class *plug_class); + +bool plug_port_prepare(const struct plug_class *, + const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); +void plug_port_finish(const struct plug_class *, + const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); +void plug_port_ctx_destroy(const struct plug_class *, + const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); + +struct ovsdb_idl; + +void plug_register_ovs_idl(struct ovsdb_idl *ovs_idl); +void plug_run(struct plug_ctx_in *, struct plug_ctx_out *); +bool plug_handle_port_binding_changes(struct plug_ctx_in *, + struct plug_ctx_out *); +bool plug_handle_ovs_interface_changes(struct plug_ctx_in *, + struct plug_ctx_out *); +void plug_finish_deleted(struct shash *deleted_iface_ids); +void plug_finish_changed(struct shash *changed_iface_ids); + +#ifdef __cplusplus +} +#endif + +#endif /* plug.h */ diff --git a/controller/test-plug.c b/controller/test-plug.c new file mode 100644 index 000000000..a6c4b8062 --- /dev/null +++ b/controller/test-plug.c @@ -0,0 +1,70 @@ +/* Copyright (c) 2021, Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "plug.h" +#include "plug-provider.h" +#include "smap.h" +#include "sset.h" +#include "tests/ovstest.h" + +static void +test_plug(struct ovs_cmdl_context *ctx OVS_UNUSED) +{ + const struct plug_class *plug_class; + + ovs_assert(plug_provider_unregister("dummy") == EINVAL); + + ovs_assert(!plug_provider_register(&plug_dummy_class)); + plug_class = plug_provider_get("dummy"); + ovs_assert(plug_provider_register(&plug_dummy_class) == EEXIST); + + ovs_assert(sset_contains(plug_get_maintained_iface_options(plug_class), + "plug-dummy-option")); + + struct plug_port_ctx_in ctx_in = { + .op_type = PLUG_OP_CREATE, + .lport_name = "lsp1", + .lport_options = SMAP_INITIALIZER(&ctx_in.lport_options), + }; + struct plug_port_ctx_out ctx_out; + plug_port_prepare(plug_class, &ctx_in, &ctx_out); + ovs_assert(!strcmp(ctx_out.name, "lsp1")); + ovs_assert(!strcmp(ctx_out.type, "internal")); + ovs_assert(!strcmp(smap_get( + ctx_out.iface_options, "plug-dummy-option"), "value")); + + plug_port_finish(plug_class, &ctx_in, &ctx_out); + plug_port_ctx_destroy(plug_class, &ctx_in, &ctx_out); + plug_provider_destroy_all(); +} + +static void +test_plug_main(int argc, char *argv[]) +{ + set_program_name(argv[0]); + static const struct ovs_cmdl_command commands[] = { + {"run", NULL, 0, 0, test_plug, OVS_RO}, + {NULL, NULL, 0, 0, NULL, OVS_RO}, + }; + struct ovs_cmdl_context ctx; + ctx.argc = argc - 1; + ctx.argv = argv + 1; + ovs_cmdl_run_command(&ctx, commands); +} + +OVSTEST_REGISTER("test-plug", test_plug_main); diff --git a/lib/automake.mk b/lib/automake.mk index 9f9f447d5..0c320c6f9 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -4,6 +4,11 @@ lib_libovn_la_LDFLAGS = \ -Wl,--version-script=$(top_builddir)/lib/libovn.sym \ $(OVS_LIBDIR)/libopenvswitch.la \ $(AM_LDFLAGS) + +if HAVE_PLUG_PROVIDER +lib_libovn_la_LDFLAGS += $(PLUG_PROVIDER_LDFLAGS) +endif + lib_libovn_la_SOURCES = \ lib/acl-log.c \ lib/acl-log.h \ @@ -33,7 +38,10 @@ lib_libovn_la_SOURCES = \ lib/inc-proc-eng.h \ lib/lb.c \ lib/lb.h \ - lib/stopwatch-names.h + lib/stopwatch-names.h \ + lib/plug-provider.h \ + lib/plug-provider.c \ + lib/plug_providers/dummy/plug-dummy.c nodist_lib_libovn_la_SOURCES = \ lib/ovn-dirs.c \ lib/ovn-nb-idl.c \ diff --git a/lib/plug-provider.c b/lib/plug-provider.c new file mode 100644 index 000000000..e7e463423 --- /dev/null +++ b/lib/plug-provider.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "plug-provider.h" + +#include +#include +#include + +#include "openvswitch/vlog.h" +#include "openvswitch/shash.h" +#include "smap.h" +#include "sset.h" +#include "lib/inc-proc-eng.h" + +VLOG_DEFINE_THIS_MODULE(plug_provider); + +#ifdef ENABLE_PLUG +static const struct plug_class *base_plug_classes[] = { +}; +#endif + +static struct shash plug_classes = SHASH_INITIALIZER(&plug_classes); + +/* Protects the 'plug_classes' shash. */ +static struct ovs_mutex plug_classes_mutex = OVS_MUTEX_INITIALIZER; + +/* Initialize the the plug infrastructure by registering known plug classes */ +void +plug_provider_initialize(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + + if (ovsthread_once_start(&once)) { +#ifdef ENABLE_PLUG + /* Register built-in plug provider classes */ + for (int i = 0; i < ARRAY_SIZE(base_plug_classes); i++) { + plug_provider_register(base_plug_classes[i]); + } +#endif +#ifdef HAVE_PLUG_PROVIDER + /* Register external plug provider classes. + * + * Note that we cannot use the ARRAY_SIZE macro here as + * plug_provider_classes is defined in external code which is not + * available at compile time. The convention is to use a + * NULL-terminated array instead. */ + for (const struct plug_class **pp = plug_provider_classes; + pp && *pp; + pp++) + { + plug_provider_register(*pp); + } +#endif + ovsthread_once_done(&once); + } +} + +static int +plug_provider_register__(const struct plug_class *new_class) +{ + struct plug_class *plug_class; + int error; + + if (shash_find(&plug_classes, new_class->type)) { + VLOG_WARN("attempted to register duplicate plug provider: %s", + new_class->type); + return EEXIST; + } + + error = new_class->init ? new_class->init() : 0; + if (error) { + VLOG_WARN("failed to initialize %s plug class: %s", + new_class->type, ovs_strerror(error)); + return error; + } + + plug_class = xmalloc(sizeof *plug_class); + memcpy(plug_class, new_class, sizeof *plug_class); + + shash_add(&plug_classes, new_class->type, plug_class); + + return 0; +} + +/* Register the new plug provider referred to in 'new_class' and perform any + * class level initialization as specified in its plug_class. */ +int +plug_provider_register(const struct plug_class *new_class) +{ + int error; + + ovs_mutex_lock(&plug_classes_mutex); + error = plug_provider_register__(new_class); + ovs_mutex_unlock(&plug_classes_mutex); + + return error; +} + +static int +plug_provider_unregister__(const char *type) +{ + int error; + struct shash_node *node; + struct plug_class *plug_class; + + node = shash_find(&plug_classes, type); + if (!node) { + return EINVAL; + } + + plug_class = node->data; + error = plug_class->destroy ? plug_class->destroy() : 0; + if (error) { + VLOG_WARN("failed to destroy %s plug class: %s", + plug_class->type, ovs_strerror(error)); + return error; + } + + shash_delete(&plug_classes, node); + free(plug_class); + + return 0; +} + +/* Unregister the plug provider identified by 'type' and perform any class + * level de-initialization as specified in its plug_class. */ +int +plug_provider_unregister(const char *type) +{ + int error; + + ovs_mutex_lock(&plug_classes_mutex); + error = plug_provider_unregister__(type); + ovs_mutex_unlock(&plug_classes_mutex); + + return error; +} + +/* Check whether there are any plug providers registered */ +bool +plug_provider_has_providers(void) +{ + return !shash_is_empty(&plug_classes); +} + +const struct plug_class * +plug_provider_get(const char *type) +{ + struct plug_class *plug_class; + + ovs_mutex_lock(&plug_classes_mutex); + plug_class = shash_find_data(&plug_classes, type); + ovs_mutex_unlock(&plug_classes_mutex); + + return plug_class; +} + +/* Iterate over plug providers and call their run function. + * + * Returns 'true' if any of the providers run functions return 'true', 'false' + * otherwise. + * + * A return value of 'true' means that data has changed. */ +bool +plug_provider_run_all(void) +{ + struct shash_node *node, *next; + bool changed = false; + + SHASH_FOR_EACH_SAFE (node, next, &plug_classes) { + struct plug_class *plug_class = node->data; + if (plug_class->run && plug_class->run(plug_class)) { + changed = true; + } + } + return changed; +} + +/* De-initialize and unregister the plug provider classes. */ +void +plug_provider_destroy_all(void) +{ + struct shash_node *node, *next; + + SHASH_FOR_EACH_SAFE (node, next, &plug_classes) { + struct plug_class *plug_class = node->data; + plug_provider_unregister(plug_class->type); + } +} diff --git a/lib/plug-provider.h b/lib/plug-provider.h new file mode 100644 index 000000000..c26b172d2 --- /dev/null +++ b/lib/plug-provider.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLUG_PROVIDER_H +#define PLUG_PROVIDER_H 1 + +/* Interface for plug providers. + * + * A plug provider implementation performs lookup and/or initialization of + * ports, typically representor ports, using generic non-blocking hardware + * interfaces. This allows the ovn-controller to, upon the CMS's request, + * create ports and interfaces in the chassis's Open vSwitch instances (also + * known as vif plugging). + * + * This module contains the infrastructure for registering plug providers which + * may be hosted inside or outside the core OVN repository. + */ + +#include + +#include "smap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct plug_class; +struct ovsdb_idl_txn; +struct ovsrec_bridge; + +enum plug_op_type { + PLUG_OP_CREATE = 1, /* Port is created or updated */ + PLUG_OP_REMOVE, /* Port is removed from this chassis */ +}; + +struct plug_port_ctx_in { + /* Operation being performed */ + enum plug_op_type op_type; + + /* These are provided so that the plug implementation may make decisions + * based on environmental factors such as settings in the open-vswitch + * table and datapath type settings on the integration bridge. */ + const struct ovsrec_open_vswitch_table *ovs_table; + const struct ovsrec_bridge *br_int; + + /* Name of logical port, can be useful for plugging library to track any + * per port resource initialization. */ + const char *lport_name; + + /* Logical port options, while OVN will forward the contents verbatim from + * the Southbound database, the convention is for the plugging library to + * only make decisions based on the plug-* options. */ + const struct smap lport_options; + + /* When OVN knows about an existing interface record associated with this + * lport, these will be filled in with information about it. */ + const char *iface_name; + const char *iface_type; + const struct smap iface_options; +}; + +struct plug_port_ctx_out { + /* The name to use for port and interface record. */ + char *name; + + /* Type of interface to create. */ + char *type; + + /* Options to set on the interface record. */ + struct smap *iface_options; +}; + +struct plug_port_ctx { + const struct plug_class *plug; + struct plug_port_ctx_in plug_port_ctx_in; + struct plug_port_ctx_out plug_port_ctx_out; +}; + +struct plug_class { + /* Type of plugger in this class. */ + const char *type; + + /* Called when the plug provider is registered, typically at program + * startup. + * + * This function may be set to null if a plug class needs no + * initialization at registration time. */ + int (*init)(void); + + /* Called when the plug provider is unregistered, typically at program + * exit. + * + * This function may be set to null if a plug class needs no + * de-initialization at unregister time.*/ + int (*destroy)(void); + + /* Performs periodic work needed by plugger, if any is necessary. Returns + * 'true; if anything (i.e. lookup tables) changed, 'false' otherwise. + * + * A return value of 'true' will cause further processing in the + * incremental processing engine, a return value of 'false' will set the + * plug_provider_lookup node as unchanged. */ + bool (*run)(struct plug_class *); + + /* Retrieve Interface options this plugger will maintain. This set is used + * to know which items to remove when maintaining the database record. */ + const struct sset * (*plug_get_maintained_iface_options)(void); + + /* Pass plug_port_ctx_in to plug implementation to prepare for port + * creation/update. + * + * The plug implemantation can perform lookup or any per port + * initialization and should fill plug_port_ctx_out with data required for + * port/interface creation. The plug implementation should return true if + * it wants the caller to create/update a port/interface, false otherwise. + * + * Data in the plug_port_ctx_out struct is owned by the plugging library, + * and a call must be made to the plug_port_ctx_destroy callback to free + * up any allocations when done with port creation/update. + */ + bool (*plug_port_prepare)(const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); + + /* Notify plugging library that port update is done. */ + void (*plug_port_finish)(const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); + + /* Free any allocations made by the plug_port_prepare callback. */ + void (*plug_port_ctx_destroy)(const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); +}; + +extern const struct plug_class plug_dummy_class; +#ifdef HAVE_PLUG_PROVIDER +extern const struct plug_class *plug_provider_classes[]; +#endif + +void plug_provider_initialize(void); +int plug_provider_register(const struct plug_class *); +int plug_provider_unregister(const char *type); +bool plug_provider_has_providers(void); +const struct plug_class * plug_provider_get(const char *); +bool plug_provider_run_all(void); +void plug_provider_destroy_all(void); +void plug_dummy_enable(void); + +#ifdef __cplusplus +} +#endif + +#endif /* plug-provider.h */ diff --git a/lib/plug_providers/dummy/plug-dummy.c b/lib/plug_providers/dummy/plug-dummy.c new file mode 100644 index 000000000..6518d8698 --- /dev/null +++ b/lib/plug_providers/dummy/plug-dummy.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 Canonical + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "lib/plug-provider.h" + +#include + +#include "openvswitch/vlog.h" +#include "smap.h" +#include "sset.h" + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +VLOG_DEFINE_THIS_MODULE(plug_dummy); + +static struct sset plug_dummy_maintained_iface_options; + +static int +plug_dummy_init(void) +{ + sset_init(&plug_dummy_maintained_iface_options); + sset_add(&plug_dummy_maintained_iface_options, "plug-dummy-option"); + + return 0; +} + +static int +plug_dummy_destroy(void) +{ + sset_destroy(&plug_dummy_maintained_iface_options); + + return 0; +} + +static const struct sset* +plug_dummy_get_maintained_iface_options(void) +{ + return &plug_dummy_maintained_iface_options; +} + +static bool +plug_dummy_run(struct plug_class *plug) +{ + VLOG_DBG("plug_dummy_run(%p)", plug); + + return false; +} + +static bool +plug_dummy_port_prepare(const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + VLOG_DBG("plug_dummy_port_prepare: %s", ctx_in->lport_name); + + if (ctx_in->op_type == PLUG_OP_CREATE) { + size_t lport_name_len = strlen(ctx_in->lport_name); + ctx_out->name = xzalloc(IFNAMSIZ); + memcpy(ctx_out->name, ctx_in->lport_name, + (lport_name_len < IFNAMSIZ) ? lport_name_len : IFNAMSIZ - 1); + ctx_out->type = xstrdup("internal"); + ctx_out->iface_options = xmalloc(sizeof *ctx_out->iface_options); + smap_init(ctx_out->iface_options); + smap_add(ctx_out->iface_options, "plug-dummy-option", "value"); + } + + return true; +} + +static void +plug_dummy_port_finish(const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out OVS_UNUSED) +{ + VLOG_DBG("plug_dummy_port_finish: %s", ctx_in->lport_name); +} + +static void +plug_dummy_port_ctx_destroy(const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + VLOG_DBG("plug_dummy_port_ctx_destroy: %s", ctx_in->lport_name); + ovs_assert(ctx_in->op_type == PLUG_OP_CREATE); + free(ctx_out->name); + free(ctx_out->type); + smap_destroy(ctx_out->iface_options); + free(ctx_out->iface_options); +} + +const struct plug_class plug_dummy_class = { + .type = "dummy", + .init = plug_dummy_init, + .destroy = plug_dummy_destroy, + .plug_get_maintained_iface_options = + plug_dummy_get_maintained_iface_options, + .run = plug_dummy_run, + .plug_port_prepare = plug_dummy_port_prepare, + .plug_port_finish = plug_dummy_port_finish, + .plug_port_ctx_destroy = plug_dummy_port_ctx_destroy, +}; + +void +plug_dummy_enable(void) +{ + plug_provider_register(&plug_dummy_class); +} + diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml index 3d2910358..152367a89 100644 --- a/ovn-architecture.7.xml +++ b/ovn-architecture.7.xml @@ -67,8 +67,9 @@
  • One or more (usually many) hypervisors. Hypervisors must run Open vSwitch and implement the interface described in - Documentation/topics/integration.rst in the OVN source tree. - Any hypervisor platform supported by Open vSwitch is acceptable. + Documentation/topics/integration.rst in the Open vSwitch + source tree. Any hypervisor platform supported by Open vSwitch is + acceptable.
  • @@ -318,11 +319,19 @@
  • On a hypervisor, any VIFs that are to be attached to logical networks. - The hypervisor itself, or the integration between Open vSwitch and the - hypervisor (described in - Documentation/topics/integration.rst) takes care of this. - (This is not part of OVN or new to OVN; this is pre-existing integration - work that has already been done on hypervisors that support OVS.) + For instances connected through software emulated ports such as TUN/TAP + or VETH pairs, the hypervisor itself will normally create ports and plug + them into the integration bridge. For instances connected through + representor ports, typically used with hardware offload, the + ovn-controller may on CMS direction consult a plugging + provider library for representor port lookup and plug them into the + integration bridge (please refer to + Documentation/topics/plugging-providers.rst for more + information). In both cases the conventions described in + Documentation/topics/integration.rst in the Open vSwitch + source tree is followed to ensure mapping between OVN logical port and + VIF. (This is pre-existing integration work that has already been done + on hypervisors that support OVS.)
  • @@ -921,12 +930,12 @@ Eventually, a user powers on the VM that owns the VIF. On the hypervisor where the VM is powered on, the integration between the hypervisor and Open vSwitch (described in - Documentation/topics/integration.rst) adds the VIF to the OVN - integration bridge and stores vif-id in - external_ids:iface-id to indicate that the - interface is an instantiation of the new VIF. (None of this code is new - in OVN; this is pre-existing integration work that has already been done - on hypervisors that support OVS.) + Documentation/topics/integration.rst in the Open vSwitch + source tree) adds the VIF to the OVN integration bridge and stores + vif-id in external_ids:iface-id to + indicate that the interface is an instantiation of the new VIF. (None of + this code is new in OVN; this is pre-existing integration work that has + already been done on hypervisors that support OVS.)
  • diff --git a/tests/automake.mk b/tests/automake.mk index c4a7c0a5b..982db4984 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -38,7 +38,8 @@ TESTSUITE_AT = \ tests/ovn-ipam.at \ tests/ovn-features.at \ tests/ovn-lflow-cache.at \ - tests/ovn-ipsec.at + tests/ovn-ipsec.at \ + tests/ovn-plug.at SYSTEM_KMOD_TESTSUITE_AT = \ tests/system-common-macros.at \ @@ -243,13 +244,23 @@ tests_ovstest_SOURCES = \ tests/test-ovn.c \ controller/test-lflow-cache.c \ controller/test-ofctrl-seqno.c \ + controller/test-plug.c \ lib/test-ovn-features.c \ northd/test-ipam.c tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \ $(OVS_LIBDIR)/libopenvswitch.la lib/libovn.la \ + controller/binding.$(OBJEXT) \ + controller/encaps.$(OBJEXT) \ + controller/ha-chassis.$(OBJEXT) \ + controller/if-status.$(OBJEXT) \ controller/lflow-cache.$(OBJEXT) \ + controller/local_data.$(OBJEXT) \ + controller/lport.$(OBJEXT) \ controller/ofctrl-seqno.$(OBJEXT) \ + controller/ovsport.$(OBJEXT) \ + controller/patch.$(OBJEXT) \ + controller/plug.$(OBJEXT) \ northd/ipam.$(OBJEXT) # Python tests. diff --git a/tests/ovn-plug.at b/tests/ovn-plug.at new file mode 100644 index 000000000..d5c6a1b6d --- /dev/null +++ b/tests/ovn-plug.at @@ -0,0 +1,8 @@ +# +# Unit tests for the lib/plug.c module. +# +AT_BANNER([OVN unit tests - plug]) + +AT_SETUP([unit test -- plugging infrastructure tests]) +AT_CHECK([ovstest test-plug run], [0], []) +AT_CLEANUP From patchwork Tue Sep 28 12:56:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533876 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=EO+6PYr6; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJffp32hYz9sXS for ; Tue, 28 Sep 2021 22:57:22 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 3824582468; Tue, 28 Sep 2021 12:57:17 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xms2bc5y4VTZ; Tue, 28 Sep 2021 12:57:15 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id D49488188D; Tue, 28 Sep 2021 12:57:13 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 5451FC0028; Tue, 28 Sep 2021 12:57:07 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1811EC0023 for ; Tue, 28 Sep 2021 12:57:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id A7B83403A7 for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp4.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id jQvMhJw0DgGz for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp4.osuosl.org (Postfix) with ESMTPS id 0FFAB403AC for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id 73E5840DD5; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833819; bh=vNySEbzHt+057bOyVvp4XvSEej1FhGpqTsYZ/qJ9WNQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EO+6PYr6+X8nQ669zRLkgUzhTdWKB2b2Th3mxzEMerzJmCftDPpDOYM9AjsYRRp0f IK4680mw/kpNoS5c8PkPF+5zhwRpALo/BXQE9wX+Tz+82SkCasqD6V1bIKGqtHrBPI gZVwt5P2qm/Hb+enM5wEGVQya4RR5sl/0KmezEzEK0bHYo0vxfVD+U4ijN8os1SxTt /HsCPWDLk2O90De1drwOP8ROSCUczhRKjovav+v1Cu+/jpfFK/ISCWduRh/4EHeMWi QT5bIRiSeUCnS8bhHC+FXZPF0pfuDYZ+o3LfkAJq7rQNYjY6kxJEtjRQqmO6M/5Q72 pYESUz64vrybQ== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:52 +0200 Message-Id: <20210928125653.7329-12-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 11/12] ovn-controller: Prepare plug provider infrastructure. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Add port by interfaces index - To be able to effectively remove ports previously plugged by us we need to look up ports by interface records. Add `enable-dummy-plug` option - To enable testing of the plugging infrastructure without building OVN with an external plugging library we include a dummy implementation which can be enabled using this command line option. Signed-off-by: Frode Nordahl --- controller/ovn-controller.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 8e35188c6..c615e8ab2 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -56,6 +56,8 @@ #include "lib/ovn-sb-idl.h" #include "lib/ovn-util.h" #include "patch.h" +#include "plug.h" +#include "plug-provider.h" #include "physical.h" #include "pinctrl.h" #include "openvswitch/poll-loop.h" @@ -3081,11 +3083,17 @@ main(int argc, char *argv[]) patch_init(); pinctrl_init(); lflow_init(); + plug_provider_initialize(); /* Connect to OVS OVSDB instance. */ struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true)); ctrl_register_ovs_idl(ovs_idl_loop.idl); + + struct ovsdb_idl_index *ovsrec_port_by_interfaces + = ovsdb_idl_index_create1(ovs_idl_loop.idl, + &ovsrec_port_col_interfaces); + ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl); /* Configure OVN SB database. */ @@ -3349,6 +3357,8 @@ main(int argc, char *argv[]) sbrec_port_binding_by_datapath); engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key", sbrec_datapath_binding_by_key); + engine_ovsdb_node_add_index(&en_ovs_port, "interfaces", + ovsrec_port_by_interfaces); struct ed_type_lflow_output *lflow_output_data = engine_get_internal_data(&en_lflow_output); @@ -3877,6 +3887,7 @@ loop_done: pinctrl_destroy(); patch_destroy(); if_status_mgr_destroy(if_mgr); + plug_provider_destroy_all(); ovsdb_idl_loop_destroy(&ovs_idl_loop); ovsdb_idl_loop_destroy(&ovnsb_idl_loop); @@ -3897,6 +3908,7 @@ parse_options(int argc, char *argv[]) VLOG_OPTION_ENUMS, OVN_DAEMON_OPTION_ENUMS, SSL_OPTION_ENUMS, + OPT_ENABLE_DUMMY_PLUG, }; static struct option long_options[] = { @@ -3907,6 +3919,7 @@ parse_options(int argc, char *argv[]) STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, + {"enable-dummy-plug", no_argument, NULL, OPT_ENABLE_DUMMY_PLUG}, {NULL, 0, NULL, 0} }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); @@ -3952,6 +3965,10 @@ parse_options(int argc, char *argv[]) stream_ssl_set_ca_cert_file(optarg, true); break; + case OPT_ENABLE_DUMMY_PLUG: + plug_dummy_enable(); + break; + case '?': exit(EXIT_FAILURE); From patchwork Tue Sep 28 12:56:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1533882 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=b3LG8431; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4HJfg06GPrz9sXS for ; Tue, 28 Sep 2021 22:57:32 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 7B6A6407A2; Tue, 28 Sep 2021 12:57:30 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id UiwAv3suwWgZ; Tue, 28 Sep 2021 12:57:27 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp4.osuosl.org (Postfix) with ESMTPS id B3AB84156F; Tue, 28 Sep 2021 12:57:19 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id DCA69C0046; Tue, 28 Sep 2021 12:57:11 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2907BC002A for ; Tue, 28 Sep 2021 12:57:03 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 0D86C40555 for ; Tue, 28 Sep 2021 12:57:03 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=canonical.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id k9l6yW7XrseL for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-0.canonical.com (smtp-relay-canonical-0.canonical.com [185.125.188.120]) by smtp2.osuosl.org (Postfix) with ESMTPS id 7701A4021F for ; Tue, 28 Sep 2021 12:57:01 +0000 (UTC) Received: from frode-threadripper.. (ti0189a330-1161.bb.online.no [88.88.219.141]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-0.canonical.com (Postfix) with ESMTPSA id D532340DD6; Tue, 28 Sep 2021 12:56:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1632833820; bh=p9bcLJiz3xB55SyNyLwNQcoxtnrqLu07etSvSLRilI8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b3LG84319L777ri47a/ZZLKnf0veB9oBBmwK5qP6eU76NPG0zjhUjuvy+JLFg3DAa lbX3BfmS9bHAbxulR9wwXFeq8dU3xdeICWpYxxi8ilAW1pQJVyx805nMHjp3/aNva0 XXjwKKkQvSA1tmUdfFx8YxpDcbguM+SctiwbxBU7yQpT/3eoRfBXkrfpw5VkYAQpmB hSrmMO7IiCgrCmEgSASxdGzjGHXgUEYv5VVSiqpRybf9prW+MaaqbWPlArS7Tog8eo /XYz4NIOcgG1o01i1FjnzqvSePUT+tguKAGuEvpPYaxH5cs8cNdrZ7sIFtcT/K2wbq xF57RwaWFkefA== From: Frode Nordahl To: dev@openvswitch.org Date: Tue, 28 Sep 2021 14:56:53 +0200 Message-Id: <20210928125653.7329-13-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210928125653.7329-1-frode.nordahl@canonical.com> References: <20210928125653.7329-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v5 12/12] controller: Consider plugging of ports on CMS request. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" When OVN is linked with an appropriate plugging implementation, CMS can request OVN to plug individual lports into the local Open vSwitch instance. The port and instance record will be maintained during the lifetime of the lport and it will be removed on release of lport. Signed-off-by: Frode Nordahl --- controller/ovn-controller.c | 195 ++++++++++++++++++++++++++++++++++++ ovn-nb.xml | 21 ++++ tests/ovn-controller.at | 85 ++++++++++++++++ tests/ovn-macros.at | 2 +- 4 files changed, 302 insertions(+), 1 deletion(-) diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index c615e8ab2..f5516a879 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -937,6 +937,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) binding_register_ovs_idl(ovs_idl); bfd_register_ovs_idl(ovs_idl); physical_register_ovs_idl(ovs_idl); + plug_register_ovs_idl(ovs_idl); } #define SB_NODES \ @@ -2977,6 +2978,171 @@ flow_output_lflow_output_handler(struct engine_node *node, return true; } +static void * +en_plug_provider_lookup_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + return NULL; +} + +static void +en_plug_provider_lookup_cleanup(void *data OVS_UNUSED) +{ + +} + +static void +en_plug_provider_lookup_run(struct engine_node *node, + void *data OVS_UNUSED) +{ + VLOG_INFO("en_plug_provider_lookup_run"); + if (!plug_provider_has_providers()) { + engine_set_node_state(node, EN_UNCHANGED); + return; + } + + if (plug_provider_run_all()) { + engine_set_node_state(node, EN_UPDATED); + } else { + engine_set_node_state(node, EN_UNCHANGED); + + } +} + + +struct ed_type_plug_provider { + struct shash deleted_iface_ids; + struct shash changed_iface_ids; +}; + +static void * +en_plug_provider_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + struct ed_type_plug_provider *data = xzalloc(sizeof *data); + + shash_init(&data->deleted_iface_ids); + shash_init(&data->changed_iface_ids); + return data; +} + +static void +en_plug_provider_cleanup(void *data) +{ + struct ed_type_plug_provider *plug_provider_data = data; + + shash_destroy_free_data(&plug_provider_data->deleted_iface_ids); + shash_destroy_free_data(&plug_provider_data->changed_iface_ids); +} + +static void +init_plug_ctx(struct engine_node *node, + void *data, + struct plug_ctx_in *plug_ctx_in, + struct plug_ctx_out *plug_ctx_out) +{ + struct ovsrec_open_vswitch_table *ovs_table = + (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( + engine_get_input("OVS_open_vswitch", node)); + struct ovsrec_bridge_table *bridge_table = + (struct ovsrec_bridge_table *)EN_OVSDB_GET( + engine_get_input("OVS_bridge", node)); + const char *chassis_id = get_ovs_chassis_id(ovs_table); + const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table); + + ovs_assert(br_int && chassis_id); + + struct ovsdb_idl_index *sbrec_chassis_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_chassis", node), + "name"); + + const struct sbrec_chassis *chassis + = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id); + ovs_assert(chassis); + + struct ovsrec_interface_table *iface_table = + (struct ovsrec_interface_table *)EN_OVSDB_GET( + engine_get_input("OVS_interface", node)); + + struct sbrec_port_binding_table *pb_table = + (struct sbrec_port_binding_table *)EN_OVSDB_GET( + engine_get_input("SB_port_binding", node)); + + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( + engine_get_input("SB_port_binding", node), + "name"); + + struct ovsdb_idl_index *ovsrec_port_by_interfaces = + engine_ovsdb_node_get_index( + engine_get_input("OVS_port", node), + "interfaces"); + + struct ed_type_runtime_data *rt_data = + engine_get_input_data("runtime_data", node); + + plug_ctx_in->ovs_idl_txn = engine_get_context()->ovs_idl_txn; + plug_ctx_in->sbrec_port_binding_by_name = sbrec_port_binding_by_name; + plug_ctx_in->ovsrec_port_by_interfaces = ovsrec_port_by_interfaces; + plug_ctx_in->ovs_table = ovs_table; + plug_ctx_in->br_int = br_int; + plug_ctx_in->iface_table = iface_table; + plug_ctx_in->chassis_rec = chassis; + plug_ctx_in->port_binding_table = pb_table; + plug_ctx_in->local_bindings = &rt_data->lbinding_data.bindings; + + struct ed_type_plug_provider *plug_provider_data = data; + plug_ctx_out->deleted_iface_ids = &plug_provider_data->deleted_iface_ids; + plug_ctx_out->changed_iface_ids = &plug_provider_data->changed_iface_ids; +} + +static void +en_plug_provider_run(struct engine_node *node, + void *data) +{ + if (!plug_provider_has_providers()) { + engine_set_node_state(node, EN_UNCHANGED); + return; + } + struct plug_ctx_in plug_ctx_in; + struct plug_ctx_out plug_ctx_out; + init_plug_ctx(node, data, &plug_ctx_in, &plug_ctx_out); + plug_run(&plug_ctx_in, &plug_ctx_out); + engine_set_node_state(node, EN_UPDATED); +} + +static bool +plug_provider_port_binding_handler(struct engine_node *node, + void *data) +{ + if (!plug_provider_has_providers()) { + engine_set_node_state(node, EN_UNCHANGED); + return true; + } + struct plug_ctx_in plug_ctx_in; + struct plug_ctx_out plug_ctx_out; + init_plug_ctx(node, data, &plug_ctx_in, &plug_ctx_out); + + return plug_handle_port_binding_changes(&plug_ctx_in, &plug_ctx_out); +} + +static bool +plug_provider_ovs_interface_handler(struct engine_node *node, + void *data) +{ + if (!plug_provider_has_providers()) { + engine_set_node_state(node, EN_UNCHANGED); + return true; + } + + struct plug_ctx_in plug_ctx_in; + struct plug_ctx_out plug_ctx_out; + init_plug_ctx(node, data, &plug_ctx_in, &plug_ctx_out); + + return plug_handle_ovs_interface_changes(&plug_ctx_in, &plug_ctx_out); +} + struct ovn_controller_exit_args { bool *exiting; bool *restart; @@ -3213,6 +3379,11 @@ main(int argc, char *argv[]) ENGINE_NODE(flow_output, "flow_output"); ENGINE_NODE(addr_sets, "addr_sets"); ENGINE_NODE_WITH_CLEAR_TRACK_DATA(port_groups, "port_groups"); + /* The plug provider has two engine nodes. One that checks for and reacts + * to change to plug provider lookup tables, and another that reacts to + * change to OVS interface, OVN Port_Binding and runtime data */ + ENGINE_NODE(plug_provider_lookup, "plug_provider_lookup"); + ENGINE_NODE(plug_provider, "plug_provider"); #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR); SB_NODES @@ -3239,6 +3410,24 @@ main(int argc, char *argv[]) engine_add_input(&en_non_vif_data, &en_ovs_interface, non_vif_data_ovs_iface_handler); + engine_add_input(&en_plug_provider, &en_plug_provider_lookup, NULL); + engine_add_input(&en_plug_provider, &en_ovs_open_vswitch, NULL); + engine_add_input(&en_plug_provider, &en_ovs_bridge, NULL); + engine_add_input(&en_plug_provider, &en_sb_chassis, NULL); + engine_add_input(&en_plug_provider, &en_runtime_data, + engine_noop_handler); + engine_add_input(&en_plug_provider, &en_sb_port_binding, + plug_provider_port_binding_handler); + engine_add_input(&en_plug_provider, &en_ovs_port, + engine_noop_handler); + engine_add_input(&en_plug_provider, &en_ovs_interface, + plug_provider_ovs_interface_handler); + /* The node needs somewhere to output to run */ + engine_add_input(&en_flow_output, &en_plug_provider, + engine_noop_handler); + + + /* Note: The order of inputs is important, all OVS interface changes must * be handled before any ct_zone changes. */ @@ -3812,6 +4001,12 @@ main(int argc, char *argv[]) } } } + struct ed_type_plug_provider *plug_provider_data = engine_get_data( + &en_plug_provider); + if (plug_provider_data) { + plug_finish_deleted(&plug_provider_data->deleted_iface_ids); + plug_finish_changed(&plug_provider_data->changed_iface_ids); + } } ovsdb_idl_track_clear(ovnsb_idl_loop.idl); diff --git a/ovn-nb.xml b/ovn-nb.xml index 390cc5a44..2774eb255 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1028,6 +1028,27 @@ DHCP reply.

    + + + + If set, OVN will attempt to perform plugging of this VIF. In order + to get this port plugged by the OVN controller, OVN must be built + with support for VIF plugging. The default behavior is for the CMS + to do the VIF plugging. Each plug provider have their own options + namespaced by name, for example "plug:representor:key". + + Please refer to the plug provider documentation located in + Documentation/topics/plug_providers/ for more information. + + + + Requested MTU for plugged interfaces. When set the OVN controller + will fill the column + of the Open vSwitch database's + table. This in turn will + make OVS vswitchd update the MTU of the linked interface. + + diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at index a1169c569..2400aa1b7 100644 --- a/tests/ovn-controller.at +++ b/tests/ovn-controller.at @@ -795,6 +795,91 @@ OVN_CLEANUP_SBOX([hv]) OVN_CLEANUP_VSWITCH([main]) as ovn-sb OVS_APP_EXIT_AND_WAIT([ovsdb-server]) +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn-controller - plugging]) +AT_KEYWORDS([plugging]) + +ovn_start + +net_add n1 +sim_add hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovn-appctl -t ovn-controller vlog/set + +sim_add hv2 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +ovn-appctl -t ovn-controller vlog/set + +check ovn-nbctl ls-add lsw0 +check ovn-nbctl lsp-add lsw0 lsp1 +check ovn-nbctl lsp-set-addresses lsp1 "f0:00:00:00:00:01 172.16.0.101" +check ovn-nbctl --wait=sb set Logical_Switch_Port lsp1 \ + options:requested-chassis=hv1 \ + options:plug-type=dummy \ + options:plug-mtu-request=42 + +check ovn-nbctl lsp-add lsw0 lsp2 +check ovn-nbctl lsp-set-addresses lsp2 "f0:00:00:00:00:02 172.16.0.102" +check ovn-nbctl --wait=sb set Logical_Switch_Port lsp2 \ + options:requested-chassis=hv2 \ + options:plug-type=dummy \ + options:plug-mtu-request=42 + +wait_column "true" Port_Binding up logical_port=lsp1 +wait_column "true" Port_Binding up logical_port=lsp2 + +as hv1 + +# Check that the lport was plugged +AT_CHECK([as hv1 ovs-vsctl find interface name=lsp1 options:plug-dummy-option=value | grep -q "options.*value"]) +AT_CHECK([as hv1 ovs-vsctl find interface name=lsp1 mtu_request=42 | grep -q "mtu_request.*42"]) + +# Check that updating the lport updates the local iface +check ovn-nbctl --wait=sb set Logical_Switch_Port lsp1 \ + options:requested-chassis=hv1 \ + options:plug-type=dummy \ + options:plug-mtu-request=43 +OVS_WAIT_UNTIL([ + as hv1 ovs-vsctl find interface mtu_request=43 | grep -q "mtu_request.*43" +]) + +# Check that local modification of iface will trigger ovn-controller to update +# the iface record +iface_uuid=$(as hv1 ovs-vsctl --bare --columns _uuid find interface name=lsp1) +as hv1 ovs-vsctl set interface ${iface_uuid} mtu_request=44 +OVS_WAIT_UNTIL([ + as hv1 ovs-vsctl find interface mtu_request=43 | grep -q "mtu_request.*43" +]) + +lsp_uuid=$(ovn-nbctl --bare --columns _uuid find Logical_Switch_Port name=lsp1) +iface_uuid=$(as hv1 ovs-vsctl --bare --columns _uuid find Interface name=lsp1) +# Check that pointing requested-chassis somewhere else will unplug the port +check ovn-nbctl --wait=sb set Logical_Switch_Port lsp1 \ + options:requested-chassis=non-existent-chassis +OVS_WAIT_UNTIL([ + grep -q "Releasing lport lsp1" hv1/ovn-controller.log +]) +OVS_WAIT_UNTIL([ + grep -q "Unplugging port lsp1" hv1/ovn-controller.log +]) +as hv1 ovs-vsctl find interface name=lsp1 +OVS_WAIT_UNTIL([ + ! as hv1 ovs-vsctl get Interface ${iface_uuid} _uuid +]) + +# Check that removing an lport will unplug it +lsp_uuid=$(ovn-nbctl --bare --columns _uuid find Logical_Switch_Port name=lsp2) +iface_uuid=$(as hv2 ovs-vsctl --bare --columns _uuid find Interface name=lsp1) +check ovn-nbctl --wait=sb lsp-del ${lsp_uuid} +OVS_WAIT_UNTIL([ + ! as hv2 ovs-vsctl get Interface ${iface_uuid} _uuid +]) +OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP ]) diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at index f06f2e68e..b3c2d72fa 100644 --- a/tests/ovn-macros.at +++ b/tests/ovn-macros.at @@ -327,7 +327,7 @@ ovn_az_attach() { -- --may-exist add-br br-int \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \ || return 1 - start_daemon ovn-controller || return 1 + start_daemon ovn-controller --enable-dummy-plug || return 1 } # ovn_attach NETWORK BRIDGE IP [MASKLEN]