From patchwork Thu Aug 19 11:08: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: 1518593 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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=) Authentication-Results: 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=lDfBmmU/; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4Gr28l1bTQz9t0T for ; Thu, 19 Aug 2021 21:09:27 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id AF9EB80E43; Thu, 19 Aug 2021 11:09:21 +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 T3hFCw1Vn8Rs; Thu, 19 Aug 2021 11:09:17 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id BCC1180F30; Thu, 19 Aug 2021 11:09:16 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id C8AA4C0024; Thu, 19 Aug 2021 11:09:14 +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 E4889C000E for ; Thu, 19 Aug 2021 11:09:13 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id D269C605F2 for ; Thu, 19 Aug 2021 11:09:13 +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 yAAsNxkrN9y7 for ; Thu, 19 Aug 2021 11:09:09 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp3.osuosl.org (Postfix) with ESMTPS id 2AA4C60605 for ; Thu, 19 Aug 2021 11:09:09 +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-1.canonical.com (Postfix) with ESMTPSA id DBA67411F3 for ; Thu, 19 Aug 2021 11:09:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371344; bh=lKDaNUTWQS6rq6fSi+S/gVwSK0sMglX3SHo7OePRYlc=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lDfBmmU/IFdHRwXha9IEGB0jZjLlNfeB6EmYZiHL2oP0rtGbhNVBk00/LSBYrshia Vr810k+bd1+Rb1kZznLcnxz54JoignK+HfIFhYucjGJjkDBVlA5RU4iLoK2MreHyYM eMli9Z3FlXguCmikB1KQe/b4iZvmIOe/9GN7sIvSQ5fr7DyMhb/huXEheeyBOwNvEz dQWCX5sD66lTy8x4uq3VrVDz4cTlVephEV8ArTA+klcjG88jcMNjJCRlZRebEh+aCl njFy8MVZM0BiS2pNkWD0KJa887Hfj20BYgMRfK3KO7X3WGlbR619dl4uiiKjtDo+qh E7Gxnm4EOJWgA== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:51 +0200 Message-Id: <20210819110857.2229769-2-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 1/7] ovn-sb: Add plugged_by 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 use of optional plugging support we add a new plugged_by column with weakRef to the Chassis table. The ovn-controller can monitor this column and only process events for its chassis UUID. ovn-northd will fill this column with UUID of Chassis referenced in Logical_Switch_Port options:requested-chassis when options:plug-type is defined. Signed-off-by: Frode Nordahl --- northd/ovn-northd.c | 31 ++++++++++++++++++++++++++++++ ovn-nb.xml | 38 ++++++++++++++++++++++++++++++++++++ ovn-sb.ovsschema | 10 +++++++--- ovn-sb.xml | 15 +++++++++++++++ tests/ovn-northd.at | 47 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 3 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 3d8e21a4f..a66f92ddd 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -3222,6 +3222,35 @@ 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 *plug_type; /* May be NULL. */ + const char *requested_chassis; /* May be NULL. */ + bool reset_plugged_by = false; + plug_type = smap_get(&op->nbsp->options, "plug-type"); + requested_chassis = smap_get(&op->nbsp->options, + "requested-chassis"); + if (plug_type && requested_chassis) { + const struct sbrec_chassis *chassis; /* May be NULL. */ + chassis = chassis_lookup_by_name(sbrec_chassis_by_name, + requested_chassis); + if (chassis) { + sbrec_port_binding_set_plugged_by(op->sb, chassis); + } else { + reset_plugged_by = 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->plugged_by) { + reset_plugged_by = true; + } + if (reset_plugged_by) { + sbrec_port_binding_set_plugged_by(op->sb, NULL); + } } else { const char *chassis = NULL; if (op->peer && op->peer->od && op->peer->od->nbr) { @@ -15066,6 +15095,8 @@ main(int argc, char *argv[]) add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_nat_addresses); 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_plugged_by); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_gateway_chassis); ovsdb_idl_add_column(ovnsb_idl_loop.idl, diff --git a/ovn-nb.xml b/ovn-nb.xml index 8a942b54c..2812a0d11 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1020,6 +1020,44 @@ DHCP reply.

+ + +

+ These options apply to logical ports with + and + set. +

+ + + If set, OVN will attempt to 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. + Supported values: representor + + + + MAC address for identifying PF device. When + is also set, this + option is used to identify PF to use as base to locate the correct + VF representor port. When + is not set this + option is used to locate a PF representor port. + + + + Logical VF number relative to PF device specified in + . + + + + 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/ovn-sb.ovsschema b/ovn-sb.ovsschema index e5ab41db9..4df326ff4 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": "888060012 26935", "tables": { "SB_Global": { "columns": { @@ -232,7 +232,11 @@ "external_ids": {"type": {"key": "string", "value": "string", "min": 0, - "max": "unlimited"}}}, + "max": "unlimited"}}, + "plugged_by": {"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 687555c47..d32e9b6f2 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2983,6 +2983,21 @@ tcp.flags = RST; + + Chassis that should plug this port, this column must be a + record. This is populated by + ovn-northd when the and is defined. 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. + diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 93bb0e1da..5324f484b 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -5208,3 +5208,50 @@ AT_CHECK([grep lr_in_gw_redirect lrflows | grep cr-DR | sort], [0], [dnl AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([check requested-chassis and plug-type fills plugged_by col]) +AT_KEYWORDS([plugging]) +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 +check ovn-sbctl chassis-add ch3 geneve 127.0.0.4 + +wait_row_count Chassis 3 + +ch1_uuid=`ovn-sbctl --bare --columns _uuid find Chassis name="ch1"` +ch2_uuid=`ovn-sbctl --bare --columns _uuid find Chassis name="ch2"` +ch3_uuid=`ovn-sbctl --bare --columns _uuid find Chassis name="ch3"` + +ovn-nbctl ls-add S1 +ovn-nbctl --wait=sb lsp-add S1 S1-vm1 + +ovn-nbctl --wait=sb set logical_switch_port S1-vm1 \ + options:requested-chassis=ch1 + +wait_row_count Port_Binding 1 logical_port=S1-vm1 plugged_by!=$ch1_uuid + +ovn-nbctl --wait=sb set logical_switch_port S1-vm1 \ + options:plug-type=representor + +wait_row_count Port_Binding 1 logical_port=S1-vm1 plugged_by=$ch1_uuid + +ovn-nbctl --wait=sb set logical_switch_port S1-vm1 \ + options:requested-chassis=ch2 options:plug-type=representor + +wait_row_count Port_binding 1 logical-port=S1-vm1 plugged_by=$ch2_uuid + +ovn-nbctl --wait=sb set logical_switch_port S1-vm1 \ + options:requested-chassis=ch3 options:plug-type=representor + +wait_row_count Port_binding 1 logical-port=S1-vm1 plugged_by=$ch3_uuid + +ovn-nbctl --wait=sb remove logical_switch_port S1-vm1 \ + options plug-type=representor + +wait_row_count Port_binding 1 logical-port=S1-vm1 plugged_by!=$ch3_uuid + +AT_CLEANUP +]) From patchwork Thu Aug 19 11:08: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: 1518594 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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=) Authentication-Results: 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=Qil5mg3m; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4Gr28n75Byz9t18 for ; Thu, 19 Aug 2021 21:09:29 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 9459F42525; Thu, 19 Aug 2021 11:09:26 +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 9ATgHnLJV-vA; Thu, 19 Aug 2021 11:09:21 +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 190B0407DF; Thu, 19 Aug 2021 11:09:18 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 3F868C002B; Thu, 19 Aug 2021 11:09:17 +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 312CBC0022 for ; Thu, 19 Aug 2021 11:09:14 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 1E8FC80EC0 for ; Thu, 19 Aug 2021 11:09:14 +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 vZ-9Tv1TSxVe for ; Thu, 19 Aug 2021 11:09:09 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp1.osuosl.org (Postfix) with ESMTPS id 33F8480EC5 for ; Thu, 19 Aug 2021 11:09:08 +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-1.canonical.com (Postfix) with ESMTPSA id 2FD98411F4 for ; Thu, 19 Aug 2021 11:09:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371344; bh=Ju+iF58/m4tSnfmLdxzGT2ocB+xm5t2Nnpb5OFDScBI=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Qil5mg3m2ThtFZfQKtHgL43GVYth5M3h21iJWvEtNIByPCMKENhXlHpfyWIS9S/An neOyZFgSz/7BXExITKbHgvvjU1qbNzu8TtYJkmWBHD2g04WF/8I5mWbTxyjr/qemu9 nwbuLKQdcdmYwOjt4eQIfJ7rpeLq0VqMPC7zyU3GIhzLyGdgsMx/R1sEBhZTd87Fub zYi6kopfzal5iEp1o9UuAnYKraOfpL2HgtXUA/HnhCLuRN5qz50CnyGfUeX4ar6n7u Z4hxYnXfb4AB3ZgHsp8Q5OOhqMpXe6JFBtkvKYsOUYpquVwV64O2F0mEsgofZq7DUS RzL73+tQ/aLmg== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:52 +0200 Message-Id: <20210819110857.2229769-3-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 2/7] northd-ddlog: Handle Port_Binding:plugged_by. 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 patch brings northd-ddlog up to speed with the same feature added to the C version of northd. Signed-off-by: Frode Nordahl --- northd/ovn_northd.dl | 68 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index 9cf4c373b..45132cf98 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -105,7 +105,6 @@ sb::Out_Datapath_Binding(uuid, tunkey, load_balancers, external_ids) :- */ var load_balancers = set_empty(). - /* Proxy table for Out_Datapath_Binding: contains all Datapath_Binding fields, * except tunnel id, which is allocated separately (see PortTunKeyAllocation). */ relation OutProxy_Port_Binding ( @@ -120,10 +119,11 @@ relation OutProxy_Port_Binding ( tag: Option, mac: Set, nat_addresses: Set, - external_ids: Map + external_ids: Map, + plugged_by: 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, @@ -135,7 +135,8 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, .tag = tag, .mac = lsp.addresses, .nat_addresses = set_empty(), - .external_ids = eids) :- + .external_ids = eids, + .plugged_by = None) :- sp in &SwitchPort(.lsp = lsp, .sw = sw), SwitchPortNewDynamicTag(lsp._uuid, opt_tag), var tag = match (opt_tag) { @@ -160,6 +161,53 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, options }. +/* Case 1b: Create a Port_Binding per logical switch port that is not of type "router" and has options "plug-type" and "requested-chassis" */ +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, + .plugged_by = plugged_by) :- + 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 != "router", + var eids = { + var eids = lsp.external_ids; + match (lsp.external_ids.get("neutron:port_name")) { + None -> (), + Some{name} -> eids.insert("name", name) + }; + eids + }, + var options = { + var options = lsp.options; + match (sw.other_config.get("vlan-passthru")) { + Some{"true"} -> options.insert("vlan-passthru", "true"), + _ -> () + }; + options + }, + var chassis_name = match (options.get("plug-type")) { + Some{_} -> match(options.get("requested-chassis")) { + Some{chassis_name} -> chassis_name, + None -> "", + }, + None -> "", + }, + chassis_name != "", + chassis_rec in sb::Chassis(.name = chassis_name), + var plugged_by = Some{chassis_rec._uuid}. /* Case 2: Create a Port_Binding per logical switch port of type "router" */ OutProxy_Port_Binding(._uuid = lsp._uuid, @@ -173,7 +221,8 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, .tag = None, .mac = lsp.addresses, .nat_addresses = nat_addresses, - .external_ids = eids) :- + .external_ids = eids, + .plugged_by = None) :- &SwitchPort(.lsp = lsp, .sw = sw, .peer = peer), var eids = { var eids = lsp.external_ids; @@ -263,7 +312,8 @@ OutProxy_Port_Binding(._uuid = lrp._uuid, .tag = None, // always empty for router ports .mac = set_singleton("${lrp.mac} ${lrp.networks.join(\" \")}"), .nat_addresses = set_empty(), - .external_ids = lrp.external_ids) :- + .external_ids = lrp.external_ids, + .plugged_by = None) :- rp in &RouterPort(.lrp = lrp, .router = router, .peer = peer), RouterPortRAOptionsComplete(lrp._uuid, options0), (var __type, var options1) = match (router.options.get("chassis")) { @@ -471,7 +521,8 @@ OutProxy_Port_Binding(// lrp._uuid is already in use; generate a new UUID by .tag = None, //always empty for router ports .mac = set_singleton("${lrp.mac} ${lrp.networks.join(\" \")}"), .nat_addresses = set_empty(), - .external_ids = lrp.external_ids) :- + .external_ids = lrp.external_ids, + .plugged_by = None) :- DistributedGatewayPort(lrp, lr_uuid), DistributedGatewayPortHAChassisGroup(lrp, hacg_uuid), var redirect_type = match (lrp.options.get("redirect-type")) { @@ -516,7 +567,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}, + .plugged_by = pbinding.plugged_by) :- pbinding in OutProxy_Port_Binding(), PortTunKeyAllocation(pbinding._uuid, tunkey), QueueIDAllocation(pbinding._uuid, qid), From patchwork Thu Aug 19 11:08: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: 1518596 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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=) Authentication-Results: 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=Q8E9xHON; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4Gr28s6cngz9t18 for ; Thu, 19 Aug 2021 21:09:33 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 05A8B4252C; Thu, 19 Aug 2021 11:09:27 +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 ZVo-bCf7LUGD; Thu, 19 Aug 2021 11:09:21 +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 48733407F2; Thu, 19 Aug 2021 11:09:19 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0EFC4C0022; Thu, 19 Aug 2021 11:09:18 +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 D1338C0025 for ; Thu, 19 Aug 2021 11:09:14 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id ABCF540781 for ; Thu, 19 Aug 2021 11:09:14 +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 s4pDcfBO7BMv for ; Thu, 19 Aug 2021 11:09:09 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp4.osuosl.org (Postfix) with ESMTPS id 367D0407AA for ; Thu, 19 Aug 2021 11:09:09 +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-1.canonical.com (Postfix) with ESMTPSA id 71519411F5 for ; Thu, 19 Aug 2021 11:09:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371344; bh=qDKddMr2hyQXl8C/ogw0w+X+UyAodDsypoaem7LcOPI=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Q8E9xHONEWU6C6CIbXF2yt7hbDoW1uti/Yc+oyrsuptJ26auwUJ08LmUojt0gLaS6 juj/VDeJLoHccmwQm4E0QFYXePnaS5qWJVRI0GATI3aPFmyLGNh+XhI4O/VLyj8hY8 oO2Gb5M9VIyaLcEz0JG1XmTax1KxOcAkirdW1zlZp3qDKDRSVeaj2IGM2DI9ohJ1Ng ZSG5zqm34WuyBmM+uYrZ154Zl8sULjjNDR/53I6DeQ85neUBhQSTY71Cu1YBwpQZCZ cjUrhZygGYDlD63CetO7q1YpTrhDc5m5EikPawT4VS48xJpCdjh1fLEN/GPK59xZZs fPWMg4jOrUMRQ== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:53 +0200 Message-Id: <20210819110857.2229769-4-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 3/7] 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 | 256 +++++++++++++++++++++++++++++++++++++++++ controller/ovsport.h | 60 ++++++++++ 3 files changed, 319 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..b1183e9ed --- /dev/null +++ b/controller/ovsport.c @@ -0,0 +1,256 @@ +/* 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 callers responsibility to ensure that no other port with the desired + * name already exist. + * + * 'iface_type' optionally specifies the type of intercace, 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_update_ports_addvalue(bridge, port); + ovsrec_bridge_verify_ports(bridge); +} + +/* Remove 'port' from 'bridge' and delete the 'port' recrd and any records + * with a weakRef to it. */ +void +ovsport_remove(const struct ovsrec_bridge *bridge, + const struct ovsrec_port *port) +{ + ovsrec_bridge_update_ports_delvalue(bridge, port); + ovsrec_bridge_verify_ports(bridge); + 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 + * exist 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 exist 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_set_type(iface, type); + } + + if (external_ids && mnt_external_ids) { + 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) { + update_interface_smap_column( + iface, external_ids, &iface->external_ids, + ovsrec_interface_update_external_ids_setkey); + } + + if (options && mnt_options) { + maintain_interface_smap_column( + iface, mnt_options, options, &iface->options, + ovsrec_interface_update_options_setkey, + ovsrec_interface_update_options_delkey); + } else if (options) { + 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_set_mtu_request( + iface, &mtu_request, mtu_request > 0); + } + } else if (iface->mtu_request) { + 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)) + || !db_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 ((db_value && value && strcmp(db_value, value)) + || (value && !db_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 Thu Aug 19 11:08:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1518592 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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=) Authentication-Results: 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=phf5kV42; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4Gr28g40QQz9t18 for ; Thu, 19 Aug 2021 21:09:21 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 44C8080C35; Thu, 19 Aug 2021 11:09: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 nv6lqdyrKQNT; Thu, 19 Aug 2021 11:09:15 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 4372580EC0; Thu, 19 Aug 2021 11:09:14 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1CC58C0010; Thu, 19 Aug 2021 11:09:14 +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 5A11DC000E for ; Thu, 19 Aug 2021 11:09:13 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 4962A80EC0 for ; Thu, 19 Aug 2021 11:09:13 +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 LyVaO3NWYTWe for ; Thu, 19 Aug 2021 11:09:09 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp1.osuosl.org (Postfix) with ESMTPS id 33F6880EC2 for ; Thu, 19 Aug 2021 11:09:09 +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-1.canonical.com (Postfix) with ESMTPSA id C7E1B411FB for ; Thu, 19 Aug 2021 11:09:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371344; bh=wfhStxVbg/X5SMKKSdhAa+c99+UoZWKNxffSDVizhvk=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=phf5kV42RIPINepuyOJc/ABjKm9SHZgELzdzoikpyutDeOuL1/BuLezW8HVA1Ro7R 0oOXBVheLd3cMuNg/G3lCMj8EQfKT7DHoi0rNJJyyxOaAabMYD5K5cvKkDRhpZNdSF iOQYAtvrnrzoIg0nUQmGrXHg7o5NY+0B4DMgeIryybmuQ6WyoWbQMeECkGdsmvmwd4 QkMG/NVqizepFE9uQVTfuJ2L1elcRyfSpj9BpyQo3HC/ejQoS1ClB501Tu0pJYZRoU QLkniGj2BR+Tr/tkTKAge9pc9dHGQX3LKs9AoEZlI9yYNBAjkJZdUERW787V83HS7z M/xG2K+2Fvynw== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:54 +0200 Message-Id: <20210819110857.2229769-5-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 4/7] 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 Thu Aug 19 11:08:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1518599 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: 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=maZPhwSO; dkim-atps=neutral Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (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 ozlabs.org (Postfix) with ESMTPS id 4Gr28y4YL9z9t0T for ; Thu, 19 Aug 2021 21:09:38 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 3A50740740; Thu, 19 Aug 2021 11:09:36 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org 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 T9vhnDai2s4C; Thu, 19 Aug 2021 11:09:30 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp2.osuosl.org (Postfix) with ESMTPS id EFC27404E5; Thu, 19 Aug 2021 11:09:24 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 5C0D0C0030; Thu, 19 Aug 2021 11:09:21 +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 E856CC002A for ; Thu, 19 Aug 2021 11:09:16 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id C342680F25 for ; Thu, 19 Aug 2021 11:09:16 +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 8nJlFxFBRhAP for ; Thu, 19 Aug 2021 11:09:10 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp1.osuosl.org (Postfix) with ESMTPS id A770680EB0 for ; Thu, 19 Aug 2021 11:09:10 +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-1.canonical.com (Postfix) with ESMTPSA id 170A9411FC for ; Thu, 19 Aug 2021 11:09:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371345; bh=vPGz3VEAE9DaQBI0zDX/ufAMU4wxRAK4eBu+IrLo27s=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=maZPhwSO7yLFlOxp53Kt1ejJsD+KjKjupz7cWBjnvAUnScFcUIuyPLj4XZkl3qVY8 n7WRCmfu2wTYAYnzwQqxv5E0lfyVKhC6vQQa3zIVgx9nkOcT6XBTyxZV0fEIrYz76a 3tkpWvlUbaHMMqR8tU20DAAjlb613FdApWgF7qH0CcerEEiC7sHdPcOYsXa6xeqrXt xntWf6Digk1iqFA7YLcSDuOLUBI+8d2vPRkyHbEu4adILZHbzdUwwFLPVJ6elamYgC etl02NLY06xRD01Dn4cZS6DZJTDmyvSSdJ2aY5r/UB3vYqtVDOXLQjXpX6a8JRoaT0 bxTn72QNRbFCA== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:55 +0200 Message-Id: <20210819110857.2229769-6-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 5/7] lib: Add infrastructure for plugging 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 module contains the infrastructure for registering and instantiating plugging classes which may be hosted inside or outside the core OVN repository. The data structures and functions for interacting with these plugging classes also live there. Extend build system to allow linking a externally built plugging provider. Signed-off-by: Frode Nordahl --- acinclude.m4 | 34 +++++ configure.ac | 1 + lib/automake.mk | 12 +- lib/plug-dummy.c | 144 +++++++++++++++++++ lib/plug-dummy.h | 33 +++++ lib/plug-provider.h | 109 ++++++++++++++ lib/plug.c | 342 ++++++++++++++++++++++++++++++++++++++++++++ lib/plug.h | 101 +++++++++++++ lib/test-plug.c | 80 +++++++++++ tests/automake.mk | 4 +- tests/ovn-plug.at | 8 ++ 11 files changed, 866 insertions(+), 2 deletions(-) create mode 100644 lib/plug-dummy.c create mode 100644 lib/plug-dummy.h create mode 100644 lib/plug-provider.h create mode 100644 lib/plug.c create mode 100644 lib/plug.h create mode 100644 lib/test-plug.c create mode 100644 tests/ovn-plug.at diff --git a/acinclude.m4 b/acinclude.m4 index e7f829520..ba7a01c49 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -441,3 +441,37 @@ 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 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 +]) diff --git a/configure.ac b/configure.ac index df0b98295..73e3bfe8c 100644 --- a/configure.ac +++ b/configure.ac @@ -172,6 +172,7 @@ OVS_ENABLE_SPARSE OVS_CHECK_DDLOG([0.38]) OVS_CHECK_PRAGMA_MESSAGE OVN_CHECK_OVS +OVN_CHECK_PLUG_PROVIDER OVS_CTAGS_IDENTIFIERS AC_SUBST([OVS_CFLAGS]) AC_SUBST([OVS_LDFLAGS]) diff --git a/lib/automake.mk b/lib/automake.mk index ddfe33948..086fbd62d 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -3,6 +3,11 @@ lib_libovn_la_LDFLAGS = \ $(OVS_LTINFO) \ -Wl,--version-script=$(top_builddir)/lib/libovn.sym \ $(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 \ @@ -32,7 +37,12 @@ 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.h \ + lib/plug.c \ + lib/plug-dummy.h \ + lib/plug-dummy.c nodist_lib_libovn_la_SOURCES = \ lib/ovn-dirs.c \ lib/ovn-nb-idl.c \ diff --git a/lib/plug-dummy.c b/lib/plug-dummy.c new file mode 100644 index 000000000..efa413b19 --- /dev/null +++ b/lib/plug-dummy.c @@ -0,0 +1,144 @@ +/* + * 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-dummy.h" +#include "plug-provider.h" +#include "plug.h" + +#include + +#include "openvswitch/vlog.h" +#include "smap.h" +#include "sset.h" + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +VLOG_DEFINE_THIS_MODULE(plug_dummy); + +struct plug_dummy { + struct plug plug; +}; + +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 int +plug_dummy_open(const struct plug_class *class, struct plug **plugp) +{ + struct plug_dummy *plug; + + plug = xmalloc(sizeof *plug); + plug->plug.plug_class = class; + *plugp = &plug->plug; + + VLOG_DBG("plug_dummy_open(%p)", plug); + return 0; +} + +static int +plug_dummy_close(struct plug *plug) +{ + VLOG_DBG("plug_dummy_close(%p)", plug); + free(plug); + + return 0; +} + +static bool +plug_dummy_run(struct plug *plug) +{ + VLOG_DBG("plug_dummy_run(%p)", plug); + + return true; +} + +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", + .maintained_iface_options = &plug_dummy_maintained_iface_options, + .init = plug_dummy_init, + .destroy = plug_dummy_destroy, + .open = plug_dummy_open, + .close = plug_dummy_close, + .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_register_provider(&plug_dummy_class); +} + diff --git a/lib/plug-dummy.h b/lib/plug-dummy.h new file mode 100644 index 000000000..6ea33671e --- /dev/null +++ b/lib/plug-dummy.h @@ -0,0 +1,33 @@ +/* + * 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_DUMMY_H +#define PLUG_DUMMY_H 1 + +/* + * The dummy plugger, allows for experimenting with plugging in a sandbox */ + +#ifdef __cplusplus +extern "C" { +#endif + +void plug_dummy_enable(void); + +#ifdef __cplusplus +} +#endif + +#endif /* plug-dummy.h */ diff --git a/lib/plug-provider.h b/lib/plug-provider.h new file mode 100644 index 000000000..5ffbe088f --- /dev/null +++ b/lib/plug-provider.h @@ -0,0 +1,109 @@ +/* + * 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 + +/* Provider interface to pluggers. A plugger 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). + */ + +#include + +#include "plug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct plug { + const struct plug_class *plug_class; +}; + +struct plug_class { + /* Type of plugger in this class. */ + const char *type; + + /* Interface options this plugger will maintain. This set is used + * to know which items to remove when maintaining the database record. */ + struct sset *maintained_iface_options; + + /* 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); + + /* Creates a new plug class instance. + * + * If successful, stores a pointer to the plug instance in '*plugp' */ + int (*open)(const struct plug_class *class, struct plug **plugp); + + /* Closes plug class instance and frees associated memory. */ + int (*close)(struct plug *plug); + + /* Performs periodic work needed by plugger, if any is necessary. Returns + * true if something changed, false otherwise. + * + * Note that work performed by plugger in this function must under no + * circumstances block. */ + bool (*run)(struct plug *plug); + + /* 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 + +#ifdef __cplusplus +} +#endif + +#endif /* plug-provider.h */ diff --git a/lib/plug.c b/lib/plug.c new file mode 100644 index 000000000..8d717bb37 --- /dev/null +++ b/lib/plug.c @@ -0,0 +1,342 @@ +/* + * 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 "plug.h" + +#include +#include +#include + +#include "openvswitch/vlog.h" +#include "openvswitch/shash.h" +#include "smap.h" +#include "sset.h" + +VLOG_DEFINE_THIS_MODULE(plug); + +struct registered_plug_class { + const struct plug_class *plug_class; + int refcount; +}; +static struct shash plug_classes = SHASH_INITIALIZER(&plug_classes); +static struct shash plug_instances = SHASH_INITIALIZER(&plug_instances); + +/* Protects 'plug_classes', including the refcount. */ +static struct ovs_mutex plug_classes_mutex = OVS_MUTEX_INITIALIZER; +/* Protects 'plug_instances' */ +static struct ovs_mutex plug_instances_mutex = OVS_MUTEX_INITIALIZER; + +/* Initialize the the plug infrastructure by registering known plug classes */ +static void +plug_initialize(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + + if (ovsthread_once_start(&once)) { +#ifdef HAVE_PLUG_PROVIDER + for (const struct plug_class **pp = plug_provider_classes; + pp && *pp; + pp++) + { + plug_register_provider(*pp); + } +#endif + ovsthread_once_done(&once); + } +} + +static int +plug_register_provider__(const struct plug_class *new_class) +{ + struct registered_plug_class *rc; + 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; + } + + rc = xmalloc(sizeof *rc); + rc->plug_class = new_class; + rc->refcount = 0; + + shash_add(&plug_classes, new_class->type, rc); + + 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_register_provider(const struct plug_class *new_class) +{ + int error; + + ovs_mutex_lock(&plug_classes_mutex); + error = plug_register_provider__(new_class); + ovs_mutex_unlock(&plug_classes_mutex); + + return error; +} + +static int +plug_unregister_provider__(const char *type) +{ + int error; + struct shash_node *node; + struct registered_plug_class *rc; + + node = shash_find(&plug_classes, type); + if (!node) { + return EINVAL; + } + + rc = node->data; + if (rc->refcount) { + VLOG_WARN("attempted to unregister in use plug provider: %s", type); + return EBUSY; + } + + error = rc->plug_class->destroy ? rc->plug_class->destroy() : 0; + if (error) { + VLOG_WARN("failed to destroy %s plug class: %s", + rc->plug_class->type, ovs_strerror(error)); + return error; + } + + shash_delete(&plug_classes, node); + free(rc); + + return 0; +} + +/* Unregister the plug provider identified by 'type' and perform any class + * level de-initialization as specified in its plug_class. */ +int +plug_unregister_provider(const char *type) +{ + int error; + + plug_initialize(); + + ovs_mutex_lock(&plug_classes_mutex); + error = plug_unregister_provider__(type); + ovs_mutex_unlock(&plug_classes_mutex); + + return error; +} + +static void +plug_class_unref(struct registered_plug_class *rc) +{ + ovs_mutex_lock(&plug_classes_mutex); + ovs_assert(rc->refcount); + rc->refcount--; + ovs_mutex_unlock(&plug_classes_mutex); +} + +static struct registered_plug_class * +plug_class_lookup(const char *type) +{ + struct registered_plug_class *rc; + + ovs_mutex_lock(&plug_classes_mutex); + rc = shash_find_data(&plug_classes, type); + if (rc) { + rc->refcount++; + } + ovs_mutex_unlock(&plug_classes_mutex); + + return rc; +} + +static int +plug_open__(const char *type, struct plug **plugp) +{ + struct plug *plug = NULL; + int error; + struct registered_plug_class *rc; + + plug_initialize(); + rc = plug_class_lookup(type); + if (!rc) { + VLOG_WARN("unable to open plug provider of unknown type: %s", type); + error = EINVAL; + goto out; + } + + error = rc->plug_class->open(rc->plug_class, &plug); + if (error) { + plug_class_unref(rc); + } + +out: + *plugp = error ? NULL: plug; + return error; +} + +/* Create, or retrieve the already created instance of plug class from a + * previous call to plug_open, identified by 'type' and store a reference to it + * in '*plugp'. + * + * The plug implementation will perform any initialization and allocations it + * needs, and the plug infrastructure will store a reference to it. Subsequent + * calls to this function with the same 'type' parameter will return the same + * object, until the instance is removed with a call to plug_close. */ +int +plug_open(const char *type, struct plug **plugp) +{ + struct plug *instance = shash_find_data(&plug_instances, type); + int error; + + if (instance) { + *plugp = instance; + return 0; + } + + error = plug_open__(type, plugp); + if (error) { + return error; + } + + ovs_mutex_lock(&plug_instances_mutex); + shash_add(&plug_instances, type, *plugp); + ovs_mutex_unlock(&plug_instances_mutex); + + return 0; +} + +/* Close the plug class instance previously created by a call to 'plug_open'. + * + * The plug implementation will perform any destruction of its data and the + * plug infrastructure will remove its references to it. */ +void +plug_close(struct plug *plug) +{ + if (plug) { + ovs_mutex_lock(&plug_instances_mutex); + shash_find_and_delete(&plug_instances, plug->plug_class->type); + ovs_mutex_unlock(&plug_instances_mutex); + + struct registered_plug_class *rc; + rc = shash_find_data(&plug_classes, plug->plug_class->type); + rc->plug_class->close(plug); + plug_class_unref(rc); + } +} + +/* Close any previously instantiated plug classes and unregister the plug + * providers. */ +void +plug_destroy_all(void) +{ + struct shash_node *node, *next; + + SHASH_FOR_EACH_SAFE (node, next, &plug_instances) { + struct plug *plug = node->data; + plug_close(plug); + } + + SHASH_FOR_EACH_SAFE (node, next, &plug_classes) { + struct registered_plug_class *rc = node->data; + plug_unregister_provider(rc->plug_class->type); + } +} + +/* Iterate over previously instantiated plug classes and call their 'run' + * function if defined. + * + * If any of the instances report they have changed something this function + * will return 'true', otherwise it will return 'false'. */ +bool +plug_run_instances(void) +{ + struct shash_node *node; + bool something_changed = false; + + ovs_mutex_lock(&plug_instances_mutex); + + SHASH_FOR_EACH (node, &plug_instances) { + struct plug *instance = node->data; + if (instance->plug_class->run(instance)) { + something_changed = true; + } + } + + ovs_mutex_unlock(&plug_instances_mutex); + + return something_changed; +} + +/* Get the class level 'maintained_iface_options' set. */ +struct sset * +plug_class_get_maintained_iface_options(const struct plug *plug) +{ + return plug->plug_class->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 *plug, + 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->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 *plug, + const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + plug->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 *plug, + const struct plug_port_ctx_in *ctx_in, + struct plug_port_ctx_out *ctx_out) +{ + plug->plug_class->plug_port_ctx_destroy(ctx_in, ctx_out); +} diff --git a/lib/plug.h b/lib/plug.h new file mode 100644 index 000000000..6117bd52a --- /dev/null +++ b/lib/plug.h @@ -0,0 +1,101 @@ +/* + * 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 plugging interface. This module contains the infrastructure for + * registering and instantiating plugging classes which may be hosted inside + * or outside the core OVN repository. The data structures and functions for + * interacting with these plugging classes also live here. + */ + +#include "smap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct plug; +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; + + /* Whether the chassis uses DPDK */ + bool use_dpdk; + + /* 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; +}; + + +int plug_register_provider(const struct plug_class *); +int plug_unregister_provider(const char *type); +void plug_destroy_all(void); +int plug_open(const char *type, struct plug **); +void plug_close(struct plug *); +bool plug_run_instances(void); + +struct sset * plug_class_get_maintained_iface_options(const struct plug *); + +bool plug_port_prepare(const struct plug *, + const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); +void plug_port_finish(const struct plug *, + const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); +void plug_port_ctx_destroy(const struct plug *, + const struct plug_port_ctx_in *, + struct plug_port_ctx_out *); + +#ifdef __cplusplus +} +#endif + +#endif /* plug.h */ diff --git a/lib/test-plug.c b/lib/test-plug.c new file mode 100644 index 000000000..b17b08a70 --- /dev/null +++ b/lib/test-plug.c @@ -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. + */ + +#include +#include + +#include "plug.h" +#include "plug-dummy.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) +{ + struct plug *plug; + + ovs_assert(plug_unregister_provider("dummy") == EINVAL); + ovs_assert(plug_open("dummy", &plug) == EINVAL); + + ovs_assert(!plug_register_provider(&plug_dummy_class)); + ovs_assert(plug_register_provider(&plug_dummy_class) == EEXIST); + ovs_assert(!plug_run_instances()); + + ovs_assert(!plug_open("dummy", &plug)); + ovs_assert(plug_unregister_provider("dummy") == EBUSY); + + ovs_assert(sset_contains( + plug_class_get_maintained_iface_options(plug), + "plug-dummy-option")); + ovs_assert(plug_run_instances()); + + struct smap fake_lport_options = SMAP_INITIALIZER(&fake_lport_options); + struct plug_port_ctx_in ctx_in = { + .op_type = PLUG_OP_CREATE, + .use_dpdk = false, + .lport_name = "lsp1", + .lport_options = &fake_lport_options, + }; + struct plug_port_ctx_out ctx_out; + plug_port_prepare(plug, &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, &ctx_in, &ctx_out); + plug_port_ctx_destroy(plug, &ctx_in, &ctx_out); + plug_close(plug); + plug_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/tests/automake.mk b/tests/automake.mk index 5b890d644..ad8978541 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 \ @@ -248,6 +249,7 @@ tests_ovstest_SOURCES = \ controller/ofctrl-seqno.c \ controller/ofctrl-seqno.h \ lib/test-ovn-features.c \ + lib/test-plug.c \ northd/test-ipam.c \ northd/ipam.c \ northd/ipam.h 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 Thu Aug 19 11:08:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1518598 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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=) Authentication-Results: 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=e0iWJeqm; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4Gr28x24Mlz9t18 for ; Thu, 19 Aug 2021 21:09:37 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id D3BC74047F; Thu, 19 Aug 2021 11:09:29 +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 XBD6qgQJuj7c; Thu, 19 Aug 2021 11:09:25 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id D0F10424F7; Thu, 19 Aug 2021 11:09:20 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E70C0C0025; Thu, 19 Aug 2021 11:09:18 +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 05CB5C0027 for ; Thu, 19 Aug 2021 11:09:15 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id DC62E400F2 for ; Thu, 19 Aug 2021 11:09:14 +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 H-nGdrJdS6et for ; Thu, 19 Aug 2021 11:09:10 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp2.osuosl.org (Postfix) with ESMTPS id BF54B400BE for ; Thu, 19 Aug 2021 11:09:10 +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-1.canonical.com (Postfix) with ESMTPSA id 60FAA411FE for ; Thu, 19 Aug 2021 11:09:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371345; bh=6XY7JsEPmUNIi5B6J6yDWj/vpyHmH3TusC4uijkaSBU=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=e0iWJeqm6NRwVIt9DjSDl6PwvY7OWGGc71oJojdVjdo8M8aqFn015paVjhEOGikN1 nZ8FVtn2C8DpG0SzJnrBBpEhxLS5f8m7w+TAHZzSiz7Gl4EzwpuPB4ExpHd19tPtI9 3eyqHZo3oelyeDRYR2W4cblBmbvcjPrgRo911XXtpCakhdH2yP9Hm59tL2TqMVbfYj RmDWbuumKobgqNznhX2eiW9eb+2v5FsEMFYHv/1J08M0UyKgCjHe9yGdHM0MkoBrNv V0zBNwiaV/xupYtd/7/9aUG9yMDqFKRb6ehCSr/P1DJclbQaZL2N1ohSEOOkDfY7zi 7NRe6rhdGmz1g== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:56 +0200 Message-Id: <20210819110857.2229769-7-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 6/7] ovn-controller: Prepare plugging 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 monitor for `plugged_by` column. 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/binding.h | 1 + controller/ovn-controller.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/controller/binding.h b/controller/binding.h index f1abc4b9c..8f4521806 100644 --- a/controller/binding.h +++ b/controller/binding.h @@ -46,6 +46,7 @@ struct binding_ctx_in { struct ovsdb_idl_index *sbrec_datapath_binding_by_key; struct ovsdb_idl_index *sbrec_port_binding_by_datapath; struct ovsdb_idl_index *sbrec_port_binding_by_name; + struct ovsdb_idl_index *ovsrec_port_by_interfaces; const struct ovsrec_port_table *port_table; const struct ovsrec_qos_table *qos_table; const struct sbrec_port_binding_table *port_binding_table; diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 678419ab3..7c583b959 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -55,7 +55,9 @@ #include "lib/mcast-group-index.h" #include "lib/ovn-sb-idl.h" #include "lib/ovn-util.h" +#include "lib/plug-dummy.h" #include "patch.h" +#include "plug.h" #include "physical.h" #include "pinctrl.h" #include "openvswitch/poll-loop.h" @@ -232,6 +234,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_plugged_by(&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 @@ -1239,6 +1244,11 @@ init_binding_ctx(struct engine_node *node, engine_get_input("SB_port_binding", node), "datapath"); + struct ovsdb_idl_index *ovsrec_port_by_interfaces = + engine_ovsdb_node_get_index( + engine_get_input("OVS_port", node), + "interfaces"); + struct controller_engine_ctx *ctrl_ctx = engine_get_context()->client_ctx; b_ctx_in->ovnsb_idl_txn = engine_get_context()->ovnsb_idl_txn; @@ -1246,6 +1256,7 @@ init_binding_ctx(struct engine_node *node, b_ctx_in->sbrec_datapath_binding_by_key = sbrec_datapath_binding_by_key; b_ctx_in->sbrec_port_binding_by_datapath = sbrec_port_binding_by_datapath; b_ctx_in->sbrec_port_binding_by_name = sbrec_port_binding_by_name; + b_ctx_in->ovsrec_port_by_interfaces = ovsrec_port_by_interfaces; b_ctx_in->port_table = port_table; b_ctx_in->iface_table = iface_table; b_ctx_in->qos_table = qos_table; @@ -3099,6 +3110,11 @@ main(int argc, char *argv[]) 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. */ @@ -3362,6 +3378,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); @@ -3883,6 +3901,7 @@ loop_done: pinctrl_destroy(); patch_destroy(); if_status_mgr_destroy(if_mgr); + plug_destroy_all(); ovsdb_idl_loop_destroy(&ovs_idl_loop); ovsdb_idl_loop_destroy(&ovnsb_idl_loop); @@ -3902,6 +3921,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[] = { @@ -3912,6 +3932,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); @@ -3957,6 +3978,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 Thu Aug 19 11:08:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frode Nordahl X-Patchwork-Id: 1518597 X-Patchwork-Delegate: zhouhan@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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=) Authentication-Results: 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=Nb/Gm6OH; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4Gr28x23vVz9t0T for ; Thu, 19 Aug 2021 21:09:37 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id B08968186E; Thu, 19 Aug 2021 11:09:34 +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 QbhtciWONdjq; Thu, 19 Aug 2021 11:09:30 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 8565D809EB; Thu, 19 Aug 2021 11:09:23 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id B19ACC002D; Thu, 19 Aug 2021 11:09:20 +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 2A9F8C0028 for ; Thu, 19 Aug 2021 11:09:16 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 07B5560BD3 for ; Thu, 19 Aug 2021 11:09:16 +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 CO_A0BEixsVK for ; Thu, 19 Aug 2021 11:09:11 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from smtp-relay-canonical-1.canonical.com (smtp-relay-canonical-1.canonical.com [185.125.188.121]) by smtp3.osuosl.org (Postfix) with ESMTPS id DC1D3606E9 for ; Thu, 19 Aug 2021 11:09:10 +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-1.canonical.com (Postfix) with ESMTPSA id AF4D8411FF for ; Thu, 19 Aug 2021 11:09:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629371345; bh=e+J9y7bTB1vUdmTkTUwIkiu1EEInTPjNOrg67U29OH0=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Nb/Gm6OH6FEug/YzC1gP0cBM/qpvFBFb2xi/QznCQD48kjWGEV84j54K4zVZfQGBy cB3wwf+GU3eJpySn9iGl1MSeFkbzUC3pgGQJgLbrrykaq+oRVAnxryoX/Ly4nsZIpc XTMVguCUiPY96IXNFJnZG/9WLz3jG/ZPCRxnJIZq7RzSlMrtEZMZwDOH052jdn+d6a P7Y0HQ390vZiHRe0nBH+Z0SAYA9vGtbJIEqBWcbA194qbUg1W8EZlQk0F9xgQkaZag v/2U1F/FwFQh4OzZEtU/RfarpCWzUryQbBHo4jq9AND/wRgWnQVhSAQu+tAaGiNMUz +W/FhKh3TQgtQ== From: Frode Nordahl To: dev@openvswitch.org Date: Thu, 19 Aug 2021 13:08:57 +0200 Message-Id: <20210819110857.2229769-8-frode.nordahl@canonical.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210819110857.2229769-1-frode.nordahl@canonical.com> References: <20210819110857.2229769-1-frode.nordahl@canonical.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH ovn v3 7/7] binding: 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/binding.c | 269 +++++++++++++++++++++++++++++++++++++++- tests/ovn-controller.at | 31 +++++ tests/ovn-macros.at | 2 +- 3 files changed, 296 insertions(+), 6 deletions(-) diff --git a/controller/binding.c b/controller/binding.c index 52eb47b79..7499fd9a2 100644 --- a/controller/binding.c +++ b/controller/binding.c @@ -23,6 +23,7 @@ #include "lib/netdev.h" #include "lib/vswitch-idl.h" #include "openvswitch/hmap.h" +#include "openvswitch/uuid.h" #include "openvswitch/vlog.h" /* OVN includes. */ @@ -35,7 +36,9 @@ #include "local_data.h" #include "lport.h" #include "ovn-controller.h" +#include "ovsport.h" #include "patch.h" +#include "plug.h" VLOG_DEFINE_THIS_MODULE(binding); @@ -45,6 +48,8 @@ VLOG_DEFINE_THIS_MODULE(binding); */ #define OVN_INSTALLED_EXT_ID "ovn-installed" +#define OVN_PLUGGED_EXT_ID "ovn-plugged" + #define OVN_QOS_TYPE "linux-htb" struct qos_queue { @@ -71,10 +76,13 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl) ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_status); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options); + ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_mtu_request); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos); ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type); @@ -1046,6 +1054,18 @@ is_binding_lport_this_chassis(struct binding_lport *b_lport, b_lport->pb->chassis == chassis); } +static bool +chassis_uses_dpdk(const struct ovsrec_open_vswitch_table *ovs_table, + const struct ovsrec_bridge *br_int) +{ + const struct ovsrec_open_vswitch *cfg; + + cfg = ovsrec_open_vswitch_table_first(ovs_table); + + return (cfg && cfg->dpdk_initialized && + !strcmp(br_int->datapath_type, "netdev")); +} + static bool can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, const char *requested_chassis) @@ -1055,6 +1075,15 @@ can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec, || !strcmp(requested_chassis, chassis_rec->hostname); } +static bool +can_plug_on_this_chassis(const struct sbrec_chassis *chassis_rec, + const struct sbrec_port_binding *pb) +{ + return (pb && chassis_rec && pb->plugged_by + && uuid_equals(&chassis_rec->header_.uuid, + &pb->plugged_by->header_.uuid)); +} + /* Returns 'true' if the 'lbinding' has binding lports of type LP_CONTAINER, * 'false' otherwise. */ static bool @@ -1088,6 +1117,51 @@ release_binding_lport(const struct sbrec_chassis *chassis_rec, return true; } +static void +consider_unplug_lport(const struct sbrec_port_binding *pb, + struct binding_ctx_in *b_ctx_in, + struct local_binding *lbinding) +{ + const char *plug_type = NULL; + if (lbinding && lbinding->iface) { + plug_type = smap_get(&lbinding->iface->external_ids, + OVN_PLUGGED_EXT_ID); + } + + if (plug_type) { + const struct ovsrec_port *port = ovsport_lookup_by_interface( + b_ctx_in->ovsrec_port_by_interfaces, + (struct ovsrec_interface *) lbinding->iface); + if (port) { + struct plug *plug; + if (plug_open(plug_type, &plug)) { + 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); + return; + } + const struct plug_port_ctx_in plug_ctx_in = { + .op_type = PLUG_OP_REMOVE, + .use_dpdk = chassis_uses_dpdk(b_ctx_in->ovs_table, + b_ctx_in->br_int), + .lport_name = (const char *)pb->logical_port, + .lport_options = (const struct smap *)&pb->options, + .iface_name = lbinding->iface->name, + .iface_type = lbinding->iface->type, + .iface_options = &lbinding->iface->options, + }; + plug_port_prepare(plug, &plug_ctx_in, NULL); + VLOG_INFO("Unplugging port %s from %s for lport %s on this " + "chassis.", + port->name, b_ctx_in->br_int->name, pb->logical_port); + ovsport_remove(b_ctx_in->br_int, port); + plug_port_finish(plug, &plug_ctx_in, NULL); + } + } +} + static bool consider_vif_lport_(const struct sbrec_port_binding *pb, bool can_bind, const char *vif_chassis, @@ -1138,15 +1212,184 @@ consider_vif_lport_(const struct sbrec_port_binding *pb, if (pb->chassis == b_ctx_in->chassis_rec) { /* Release the lport if there is no lbinding. */ if (!lbinding_set || !can_bind) { - return release_lport(pb, !b_ctx_in->ovnsb_idl_txn, - b_ctx_out->tracked_dp_bindings, - b_ctx_out->if_mgr); + bool handled = release_lport(pb, !b_ctx_in->ovnsb_idl_txn, + b_ctx_out->tracked_dp_bindings, + b_ctx_out->if_mgr); + if (handled && b_lport && b_lport->lbinding) { + consider_unplug_lport(pb, b_ctx_in, b_lport->lbinding); + } + return handled; } } 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 *plug, + const struct smap *iface_external_ids, + const struct sbrec_port_binding *pb, + struct binding_ctx_in *b_ctx_in) +{ + bool ret = true; + struct plug_port_ctx_in plug_ctx_in = { + .op_type = PLUG_OP_CREATE, + .use_dpdk = chassis_uses_dpdk(b_ctx_in->ovs_table, + b_ctx_in->br_int), + .lport_name = (const char *)pb->logical_port, + .lport_options = (const struct smap *)&pb->options, + }; + struct plug_port_ctx_out plug_ctx_out; + + if (!plug_port_prepare(plug, &plug_ctx_in, &plug_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); + ret = false; + goto out; + } + + VLOG_INFO("Plugging port %s into %s for lport %s on this " + "chassis.", + plug_ctx_out.name, b_ctx_in->br_int->name, + pb->logical_port); + ovsport_create(b_ctx_in->ovs_idl_txn, b_ctx_in->br_int, + plug_ctx_out.name, plug_ctx_out.type, + NULL, iface_external_ids, + plug_ctx_out.iface_options, + get_plug_mtu_request(&pb->options)); + + plug_port_finish(plug, &plug_ctx_in, &plug_ctx_out); + +out: + plug_port_ctx_destroy(plug, &plug_ctx_in, &plug_ctx_out); + + return ret; +} + +static bool +consider_plug_lport_update__(const struct plug *plug, + const struct smap *iface_external_ids, + const struct sbrec_port_binding *pb, + struct binding_ctx_in *b_ctx_in, + struct local_binding *lbinding) +{ + bool ret = true; + struct plug_port_ctx_in plug_ctx_in = { + .op_type = PLUG_OP_CREATE, + .use_dpdk = chassis_uses_dpdk(b_ctx_in->ovs_table, + b_ctx_in->br_int), + .lport_name = (const char *)pb->logical_port, + .lport_options = (const struct smap *)&pb->options, + .iface_name = lbinding->iface->name, + .iface_type = lbinding->iface->type, + .iface_options = &lbinding->iface->options, + }; + struct plug_port_ctx_out plug_ctx_out; + + if (!plug_port_prepare(plug, &plug_ctx_in, &plug_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); + ret = false; + goto out; + } + + if (strcmp(lbinding->iface->name, plug_ctx_out.name)) { + VLOG_WARN("Attempt of incompatible change to existing " + "port detected, please recreate port: %s", + pb->logical_port); + ret = false; + goto out; + } + VLOG_DBG("updating iface for: %s", pb->logical_port); + ovsport_update_iface(lbinding->iface, plug_ctx_out.type, + iface_external_ids, + NULL, + plug_ctx_out.iface_options, + plug_class_get_maintained_iface_options( + plug), + get_plug_mtu_request(&pb->options)); + + plug_port_finish(plug, &plug_ctx_in, &plug_ctx_out); +out: + plug_port_ctx_destroy(plug, &plug_ctx_in, &plug_ctx_out); + + return ret; +} + +static bool +consider_plug_lport(const struct sbrec_port_binding *pb, + struct binding_ctx_in *b_ctx_in, + struct local_binding *lbinding) +{ + if (!b_ctx_in->chassis_rec || !b_ctx_in->br_int || !b_ctx_in->ovs_idl_txn) + { + return false; + } + bool ret = true; + + if (can_plug_on_this_chassis(b_ctx_in->chassis_rec, pb)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + const char *plug_type = smap_get(&pb->options, "plug-type"); + if (!plug_type) { + /* This should never happen, but better safe than sorry */ + VLOG_WARN_RL(&rl, + "Possible database inconsistency detected: " + "Port_Binding->plugged_by points at this chassis, " + "but Port_Binding->options:plug-type not set. " + "lport %s", + pb->logical_port); + return false; + } + + struct plug *plug; + if (plug_open(plug_type, &plug)) { + VLOG_WARN_RL(&rl, + "Unable to open plug provider for plug-type: '%s' " + "lport %s", + plug_type, pb->logical_port); + return false; + } + 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)); + ret = false; + goto out; + } + ret = consider_plug_lport_update__(plug, &iface_external_ids, pb, + b_ctx_in, lbinding); + } else { + ret = consider_plug_lport_create__(plug, &iface_external_ids, pb, + b_ctx_in); + } + } + +out: + return ret; +} + static bool consider_vif_lport(const struct sbrec_port_binding *pb, struct binding_ctx_in *b_ctx_in, @@ -1170,6 +1413,11 @@ consider_vif_lport(const struct sbrec_port_binding *pb, b_lport = local_binding_add_lport(binding_lports, lbinding, pb, LP_VIF); } + /* Consider if we should create or update local port/interface record for + * this lport. Note that a newly created port/interface will get its flows + * installed on the next loop iteration as we won't wait for OVS vSwitchd + * to configure and assign a ofport to the interface. */ + consider_plug_lport(pb, b_ctx_in, lbinding); return consider_vif_lport_(pb, can_bind, vif_chassis, b_ctx_in, b_ctx_out, b_lport, qos_map); } @@ -1563,6 +1811,10 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out) const struct sbrec_port_binding *pb; }; + /* For any open plug provider instances, perform periodic non-blocking + * maintenance */ + plug_run_instances(); + /* Run through each binding record to see if it is resident on this * chassis and update the binding accordingly. This includes both * directly connected logical ports and children of those ports @@ -2097,8 +2349,11 @@ handle_deleted_vif_lport(const struct sbrec_port_binding *pb, lbinding = b_lport->lbinding; bound = is_binding_lport_this_chassis(b_lport, b_ctx_in->chassis_rec); - /* Remove b_lport from local_binding. */ - binding_lport_delete(binding_lports, b_lport); + if (b_lport->lbinding) { + consider_unplug_lport(pb, b_ctx_in, b_lport->lbinding); + } + /* Remove b_lport from local_binding. */ + binding_lport_delete(binding_lports, b_lport); } if (bound && lbinding && lport_type == LP_VIF) { @@ -2676,6 +2931,10 @@ local_binding_handle_stale_binding_lports(struct local_binding *lbinding, handled = release_binding_lport(b_ctx_in->chassis_rec, b_lport, !b_ctx_in->ovnsb_idl_txn, b_ctx_out); + if (handled && b_lport && b_lport->lbinding) { + consider_unplug_lport(b_lport->pb, b_ctx_in, + b_lport->lbinding); + } binding_lport_delete(&b_ctx_out->lbinding_data->lports, b_lport); } diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at index e8550a5dc..c7f61457b 100644 --- a/tests/ovn-controller.at +++ b/tests/ovn-controller.at @@ -692,3 +692,34 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep userdata=0 OVN_CLEANUP([hv1]) 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 + +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.100" +check ovn-nbctl --wait=sb set Logical_Switch_Port lsp1 \ + options:requested-chassis=hv1 \ + options:plug-type=dummy \ + options:plug-mtu-request=42 + +wait_column "true" Port_Binding up logical_port=lsp1 + +as hv1 + +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"]) + +OVN_CLEANUP([hv1]) +AT_CLEANUP +]) + diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at index b5a01b2b5..f26e84f3d 100644 --- a/tests/ovn-macros.at +++ b/tests/ovn-macros.at @@ -325,7 +325,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]