From patchwork Fri Sep 27 22:34:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Han Zhou X-Patchwork-Id: 1168754 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="ZdRLBU75"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 46g6BP2sMVz9sN1 for ; Sat, 28 Sep 2019 08:37:49 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 277C51235; Fri, 27 Sep 2019 22:34:39 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 80D5811F5 for ; Fri, 27 Sep 2019 22:34:36 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-ed1-f47.google.com (mail-ed1-f47.google.com [209.85.208.47]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 4968090A for ; Fri, 27 Sep 2019 22:34:32 +0000 (UTC) Received: by mail-ed1-f47.google.com with SMTP id c4so3681399edl.0 for ; Fri, 27 Sep 2019 15:34:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=evkIwP9fxSI3VGpMDlMRzfTDyeKFeBV0J9l7917b5ro=; b=ZdRLBU75H1TapbwNvOgRd0XBCmRcf8a4vaUm9r1gp7MTC798nGw4MHNNn22KLIYhqf 9/FcJqZuCvCWgmwG07GQ6sk55j20TVWJu3Vkgb1XM+YqrL+3H7Ev/TYv9baP8GmO9HbC g4DBdILRzy50lwLbQZJQurg35XT4Tz0jd0M6vmN3f+Sg3kqMJJdwl5Q0cnYFwbO2djMw OTZtQ+O/Qb8y1BV9H181od3AhgSfqs4TjCykpP6eczg0zNdGoe9wdzbrzFAG9BeSJkQn JeI+zNfgUcUtremFe43p5YPoGsL+cPwYBiFMWTyptmZ2Z/KWYe/q6Q5PGgaGKlEHI+xR 6FxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=evkIwP9fxSI3VGpMDlMRzfTDyeKFeBV0J9l7917b5ro=; b=LNeloXvM5z2+SGjCYDdPESzoUhEieNeNibwy4bqqDAHpx2opRwCSsyoqBDhhi+L8qI hvwjvGfd6DAHMPyc7s5g2qyVZppehZE4sWYC/kwYR32uDF2VGgzmFCl2c+HH9EC8/Em1 sn6nfRpFzbBj0tfFRHzQrOmEmlWGgemZyBnCs77iiAQ/a/lKMnnO4tB096CWYmPd3llm XvX/ORMF4iRRsY/X3yoPdoe29LsIt+ZWyei81qQIIFl2vrAXsemmnH5OoVPQ8wVROoaS QwdxIeRLEA7vwrtUoeKYWnTVuY9CanE7AHn6uC8RtkKiMGppkV6xwj8OFXqBgspgJ3l4 vdOA== X-Gm-Message-State: APjAAAU0BviW7K2hc77uiKDpadlCfv9o3KbwYgjxAHRrrYOM7M2L8Ky8 m/jauCW+EZV0ZuTupOB5VM5RyIXO X-Google-Smtp-Source: APXvYqxza9g3red8mOAg+cE3pb4g7MbcldclaE321vVuXV82SDd4dO0qqxox79Z9qZ1CfiR/A43qTw== X-Received: by 2002:a17:906:9498:: with SMTP id t24mr9592718ejx.88.1569623669620; Fri, 27 Sep 2019 15:34:29 -0700 (PDT) Received: from localhost.localdomain.localdomain ([216.113.160.77]) by smtp.gmail.com with ESMTPSA id ng5sm715794ejb.9.2019.09.27.15.34.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 27 Sep 2019 15:34:29 -0700 (PDT) From: Han Zhou X-Google-Original-From: Han Zhou To: dev@openvswitch.org Date: Fri, 27 Sep 2019 15:34:19 -0700 Message-Id: <1569623665-77390-5-git-send-email-hzhou8@ebay.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1569623665-77390-1-git-send-email-hzhou8@ebay.com> References: <1569623665-77390-1-git-send-email-hzhou8@ebay.com> X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [RFC PATCH ovn 04/10] ovn-isb: Interconnection southbound DB schema and CLI. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Han Zhou This patch introduces OVN_IC_Southbound DB schema and the CLI ovn-isbctl that manages the DB. Signed-off-by: Han Zhou --- .gitignore | 3 + automake.mk | 38 ++ debian/ovn-common.install | 1 + debian/ovn-common.manpages | 2 + lib/.gitignore | 3 + lib/automake.mk | 17 +- lib/ovn-isb-idl.ann | 9 + lib/ovn-util.c | 13 + lib/ovn-util.h | 1 + ovn-isb.ovsschema | 130 +++++++ ovn-isb.xml | 588 ++++++++++++++++++++++++++++++ utilities/.gitignore | 2 + utilities/automake.mk | 5 + utilities/ovn-isbctl.c | 890 +++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 1701 insertions(+), 1 deletion(-) create mode 100644 lib/ovn-isb-idl.ann create mode 100644 ovn-isb.ovsschema create mode 100644 ovn-isb.xml create mode 100644 utilities/ovn-isbctl.c diff --git a/.gitignore b/.gitignore index 1994937..66bb101 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,9 @@ /ovn-inb.5 /ovn-inb.gv /ovn-inb.pic +/ovn-isb.5 +/ovn-isb.gv +/ovn-isb.pic /package.m4 /stamp-h1 /_build-gcc diff --git a/automake.mk b/automake.mk index 3bfbf57..85574fc 100644 --- a/automake.mk +++ b/automake.mk @@ -97,6 +97,37 @@ ovn-inb.5: \ $(srcdir)/ovn-inb.xml > $@.tmp && \ mv $@.tmp $@ +# OVN interconnection southbound E-R diagram +# +# If "python" or "dot" is not available, then we do not add graphical diagram +# to the documentation. +if HAVE_PYTHON +if HAVE_DOT +ovn-isb.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-isb.ovsschema + $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-isb.ovsschema > $@ +ovn-isb.pic: ovn-isb.gv ${OVSDIR}/ovsdb/dot2pic + $(AM_V_GEN)(dot -T plain < ovn-isb.gv | $(PYTHON) ${OVSDIR}/ovsdb/dot2pic -f 3) > $@.tmp && \ + mv $@.tmp $@ +OVN_ISB_PIC = ovn-isb.pic +OVN_ISB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_ISB_PIC) +CLEANFILES += ovn-isb.gv ovn-isb.pic +endif +endif + +# OVN interconnection southbound schema documentation +EXTRA_DIST += ovn-isb.xml +CLEANFILES += ovn-isb.5 +man_MANS += ovn-isb.5 + +ovn-isb.5: \ + ${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-isb.xml $(srcdir)/ovn-isb.ovsschema $(OVN_ISB_PIC) + $(AM_V_GEN)$(OVSDB_DOC) \ + $(OVN_ISB_DOT_DIAGRAM_ARG) \ + --version=$(VERSION) \ + $(srcdir)/ovn-isb.ovsschema \ + $(srcdir)/ovn-isb.xml > $@.tmp && \ + mv $@.tmp $@ + # Version checking for ovn-nb.ovsschema. ALL_LOCAL += ovn-nb.ovsschema.stamp ovn-nb.ovsschema.stamp: ovn-nb.ovsschema @@ -114,8 +145,15 @@ ovn-inb.ovsschema.stamp: ovn-inb.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += ovn-inb.ovsschema.stamp +# Version checking for ovn-isb.ovsschema. +ALL_LOCAL += ovn-isb.ovsschema.stamp +ovn-isb.ovsschema.stamp: ovn-isb.ovsschema + $(srcdir)/build-aux/cksum-schema-check $? $@ +CLEANFILES += ovn-isb.ovsschema.stamp + pkgdata_DATA += ovn-nb.ovsschema pkgdata_DATA += ovn-sb.ovsschema pkgdata_DATA += ovn-inb.ovsschema +pkgdata_DATA += ovn-isb.ovsschema CLEANFILES += ovn-sb.ovsschema.stamp diff --git a/debian/ovn-common.install b/debian/ovn-common.install index 9e9bcfb..8f8ab23 100644 --- a/debian/ovn-common.install +++ b/debian/ovn-common.install @@ -1,6 +1,7 @@ usr/bin/ovn-nbctl usr/bin/ovn-sbctl usr/bin/ovn-inbctl +usr/bin/ovn-isbctl usr/bin/ovn-trace usr/bin/ovn-detrace usr/share/openvswitch/scripts/ovn-ctl diff --git a/debian/ovn-common.manpages b/debian/ovn-common.manpages index 94325dd..895da8d 100644 --- a/debian/ovn-common.manpages +++ b/debian/ovn-common.manpages @@ -2,9 +2,11 @@ ovn/ovn-architecture.7 ovn/ovn-nb.5 ovn/ovn-sb.5 ovn/ovn-inb.5 +ovn/ovn-isb.5 ovn/utilities/ovn-ctl.8 ovn/utilities/ovn-nbctl.8 ovn/utilities/ovn-sbctl.8 ovn/utilities/ovn-inbctl.8 +ovn/utilities/ovn-isbctl.8 ovn/utilities/ovn-trace.8 ovn/utilities/ovn-detrace.1 diff --git a/lib/.gitignore b/lib/.gitignore index e5d9bf3..19cc9f5 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -8,4 +8,7 @@ /ovn-inb-idl.c /ovn-inb-idl.h /ovn-inb-idl.ovsidl +/ovn-isb-idl.c +/ovn-isb-idl.h +/ovn-isb-idl.ovsidl /ovn-dirs.c diff --git a/lib/automake.mk b/lib/automake.mk index 83fdbcd..dc62be7 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -31,7 +31,9 @@ nodist_lib_libovn_la_SOURCES = \ lib/ovn-sb-idl.c \ lib/ovn-sb-idl.h \ lib/ovn-inb-idl.c \ - lib/ovn-inb-idl.h + lib/ovn-inb-idl.h \ + lib/ovn-isb-idl.c \ + lib/ovn-isb-idl.h CLEANFILES += $(nodist_lib_libovn_la_SOURCES) @@ -89,3 +91,16 @@ lib/ovn-inb-idl.ovsidl: $(OVN_INB_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_INB_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ +# ovn-isb IDL +OVSIDL_BUILT += \ + lib/ovn-isb-idl.c \ + lib/ovn-isb-idl.h \ + lib/ovn-isb-idl.ovsidl +EXTRA_DIST += lib/ovn-isb-idl.ann +OVN_ISB_IDL_FILES = \ + $(srcdir)/ovn-isb.ovsschema \ + $(srcdir)/lib/ovn-isb-idl.ann +lib/ovn-isb-idl.ovsidl: $(OVN_ISB_IDL_FILES) + $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_ISB_IDL_FILES) > $@.tmp && \ + mv $@.tmp $@ + diff --git a/lib/ovn-isb-idl.ann b/lib/ovn-isb-idl.ann new file mode 100644 index 0000000..ea6efad --- /dev/null +++ b/lib/ovn-isb-idl.ann @@ -0,0 +1,9 @@ +# -*- python -*- + +# This code, when invoked by "ovsdb-idlc annotate" (by the build +# process), annotates ovn-isb.ovsschema with additional data that give +# the ovsdb-idl engine information about the types involved, so that +# it can generate more programmer-friendly data structures. + +s["idlPrefix"] = "isbrec_" +s["idlHeader"] = "\"lib/ovn-isb-idl.h\"" diff --git a/lib/ovn-util.c b/lib/ovn-util.c index 23a362c..8d7db44 100644 --- a/lib/ovn-util.c +++ b/lib/ovn-util.c @@ -363,6 +363,19 @@ default_inb_db(void) return def; } +const char * +default_isb_db(void) +{ + static char *def; + if (!def) { + def = getenv("OVN_ISB_DB"); + if (!def) { + def = xasprintf("unix:%s/ovnisb_db.sock", ovn_rundir()); + } + } + return def; +} + char * get_abs_unix_ctl_path(void) { diff --git a/lib/ovn-util.h b/lib/ovn-util.h index e31cf51..694c953 100644 --- a/lib/ovn-util.h +++ b/lib/ovn-util.h @@ -73,6 +73,7 @@ char *alloc_nat_zone_key(const struct uuid *key, const char *type); const char *default_nb_db(void); const char *default_sb_db(void); const char *default_inb_db(void); +const char *default_isb_db(void); char *get_abs_unix_ctl_path(void); struct ovsdb_idl_table_class; diff --git a/ovn-isb.ovsschema b/ovn-isb.ovsschema new file mode 100644 index 0000000..ffce8a2 --- /dev/null +++ b/ovn-isb.ovsschema @@ -0,0 +1,130 @@ +{ + "name": "OVN_IC_Southbound", + "version": "1.0.0", + "cksum": "1074410507 6100", + "tables": { + "ISB_Global": { + "columns": { + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "connections": { + "type": {"key": {"type": "uuid", + "refTable": "Connection"}, + "min": 0, + "max": "unlimited"}}, + "ssl": { + "type": {"key": {"type": "uuid", + "refTable": "SSL"}, + "min": 0, "max": 1}}, + "options": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "ipsec": {"type": "boolean"}}, + "maxRows": 1, + "isRoot": true}, + "Availability_Zone": { + "columns": { + "name": {"type": "string"}}, + "isRoot": true, + "indexes": [["name"]]}, + "Gateway": { + "columns": { + "name": {"type": "string"}, + "availability_zone": {"type": {"key": {"type": "uuid", + "refTable": "Availability_Zone"}}}, + "hostname": {"type": "string"}, + "encaps": {"type": {"key": {"type": "uuid", + "refTable": "Encap"}, + "min": 1, "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "isRoot": true, + "indexes": [["name"]]}, + "Encap": { + "columns": { + "type": {"type": {"key": { + "type": "string", + "enum": ["set", ["geneve", "stt", "vxlan"]]}}}, + "options": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}, + "ip": {"type": "string"}, + "gateway_name": {"type": "string"}}, + "indexes": [["type", "ip"]]}, + "Datapath_Binding": { + "columns": { + "transit_switch": {"type": "string"}, + "tunnel_key": { + "type": {"key": {"type": "integer", + "minInteger": 1, + "maxInteger": 16777215}}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "indexes": [["tunnel_key"]], + "isRoot": true}, + "Port_Binding": { + "columns": { + "logical_port": {"type": "string"}, + "transit_switch": {"type": "string"}, + "availability_zone": {"type": {"key": {"type": "uuid", + "refTable": "Availability_Zone"}}}, + "tunnel_key": { + "type": {"key": {"type": "integer", + "minInteger": 1, + "maxInteger": 32767}}}, + "gateway": {"type": "string"}, + "encap": {"type": {"key": {"type": "uuid", + "refTable": "Encap", + "refType": "weak"}, + "min": 0, "max": 1}}, + "address": {"type": "string"}, + "external_ids": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}}, + "indexes": [["transit_switch", "tunnel_key"], ["logical_port"]], + "isRoot": true}, + "Connection": { + "columns": { + "target": {"type": "string"}, + "max_backoff": {"type": {"key": {"type": "integer", + "minInteger": 1000}, + "min": 0, + "max": 1}}, + "inactivity_probe": {"type": {"key": "integer", + "min": 0, + "max": 1}}, + "other_config": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}, + "external_ids": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}, + "is_connected": {"type": "boolean", "ephemeral": true}, + "status": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}, + "ephemeral": true}}, + "indexes": [["target"]]}, + "SSL": { + "columns": { + "private_key": {"type": "string"}, + "certificate": {"type": "string"}, + "ca_cert": {"type": "string"}, + "bootstrap_ca_cert": {"type": "boolean"}, + "ssl_protocols": {"type": "string"}, + "ssl_ciphers": {"type": "string"}, + "external_ids": {"type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}}, + "maxRows": 1} + } +} diff --git a/ovn-isb.xml b/ovn-isb.xml new file mode 100644 index 0000000..28a69dd --- /dev/null +++ b/ovn-isb.xml @@ -0,0 +1,588 @@ + + +

+ This database holds configuration and state for interconnecting different + OVN deployments. The content of the database is populated and used by the + ovn-ic program in each OVN deployment, and not supposed to be + directly used by CMS or end user. +

+ +

+ The OVN Interconnection Southbound database is shared by + ovn-ic program in each OVN deployment. It contains + interconnection information from all related OVN deployments, and is used + as the intermediate store for each OVN deployment to exchange the + information. The ovn-ic program in each deployment is + responsible for syncing the data between this database and the its own + northbound and southbound databases. +

+ +

Database Structure

+ +

+ The OVN Interconnection Southbound database contains classes of data with + different properties, as described in the sections below. +

+ +

Availability Zone Specific Information

+ +

+ These tables contain objects that are availability zone specific. Each object + is owned and populated by one availability zone, and read by other availability + zones. +

+ +

+ The , , and tables are the availability + zone specific tables. +

+ +

Global Information

+ +

+ The data that does not belong to any specific availability zone but is + common for all availability zones. +

+ +

+ The table contains the common datapath + binding information. +

+ +

Common Columns

+

+ Each of the tables in this database contains a special column, named + external_ids. This column has the same form and purpose each + place it appears. +

+ +
+
external_ids: map of string-string pairs
+
+ Key-value pairs for use by ovn-ic. +
+
+ +

+ Interconnection Southbound configuration. This table must have exactly + one row. +

+ + + + See External IDs at the beginning of this document. + + + + + + + + + Database clients to which the Open vSwitch database server should + connect or on which it should listen, along with options for how these + connections should be configured. See the + table for more information. + + + Global SSL configuration. + + + + + Tunnel encryption configuration. If this column is set to be true, all + OVN tunnels will be encrypted with IPsec. XXX + + +
+ + +

+ Each row in this table represents an Availability Zone. Each OVN deployment + is considered an availability zone from OVN control plane perspective, with + its own central components, such as northbound and southbound databases and + ovn-northd daemon. +

+ + + A name that uniquely identifies the availability zone. + +
+ + +

+ Each row in this table represents a interconnection gateway chassis in an + availability zone. +

+ + + The name of the gateway. See column + of the OVN Southbound database's table. + + + + The availabilty zone that the gateway belongs to. + + + + The hostname of the gateway. + + + + The overall purpose of these columns is described under Common + Columns at the beginning of this document. + + + + + +

+ OVN uses encapsulation to transmit logical dataplane packets + between gateways. +

+ + + Points to supported encapsulation configurations to transmit + logical dataplane packets to this gateway. Each entry is a record that describes the configuration. + See column + of the OVN Southbound database's table. + +
+ +
+ + +

+ The column in the table refers to rows in this table to identify + how OVN may transmit logical dataplane packets to this gateway. +

+ + + The encapsulation to use to transmit packets to this gateway. + See column + of the OVN Southbound database's table. + + + + Options for configuring the encapsulation, which may be specific. See + column of the OVN Southbound database's table. + + + + The IPv4 address of the encapsulation tunnel endpoint. + + + + The name of the gateway that created this encap. + +
+ + +

+ Each row in this table represents a logical datapath for a transit + logical switch configured in the OVN Interconnection Northbound + database's table. +

+ + + The name of the transit logical switch that is configured in the OVN + Interconnection Northbound database's table. + + + +

+ The tunnel key value to which the logical datapath is bound. The key + can be generated by any ovn-ic but the same key is shared + by all availability zones so that the logical datapaths can be peered + across them. A tunnel key for transit switch datapath binding must be + globally unique. +

+ +

+ For more information about the meanings of a tunnel key, + see column of the OVN + Southbound database's + table. +

+
+ + + The overall purpose of these columns is described under Common + Columns at the beginning of this document. + + + +
+ + +

+ Each row in this table binds a logical port on the transit switch to a + physical gateway and a tunnel key. Each port on the transit switch + belongs to a specific availability zone. +

+ + + + The name of the transit switch that the corresponding logical port + belongs to. + + + + A logical port, taken from in the OVN_Northbound + database's + table. The logical port name must be unique across all availability + zones. + + + + The availability zone that the port belongs to. + + + + Points to supported encapsulation configurations to transmit + logical dataplane packets to this gateway. Each entry is a record that describes the configuration. + + + + The name of the gateway that this port is physically located. + + + +

+ A number that represents the logical port in the key (e.g. STT key or + Geneve TLV) field carried within tunnel protocol packets. The key + can be generated by any ovn-ic but the same key is + shared by all availability zones so that the packets can go through + the datapath pipelines of different availability zones. +

+

+ The tunnel ID must be unique within the scope of a logical datapath. +

+

+ For more information about tunnel key, see column of the OVN Southbound database's table. +

+
+ + +

+ The Ethernet address and IP addresses used by the corresponding logical + router port peering with the transit switch port. It is a string combined + with the value of + column followed by the values in column in table. +

+
+ +
+ + + +

+ See External IDs at the beginning of this document. +

+
+
+
+ + +

+ Configuration for a database connection to an Open vSwitch database + (OVSDB) client. +

+ +

+ This table primarily configures the Open vSwitch database server + (ovsdb-server). +

+ +

+ The Open vSwitch database server can initiate and maintain active + connections to remote clients. It can also listen for database + connections. +

+ + + +

Connection methods for clients.

+

+ The following connection methods are currently supported: +

+
+
ssl:host[:port]
+
+

+ The specified SSL port on the given host, + which can either be a DNS name (if built with unbound library) or + an IP address. A valid SSL configuration must be provided when + this form is used, this configuration can be specified via + command-line options or the table. +

+

+ If port is not specified, it defaults to 6640. +

+

+ SSL support is an optional feature that is not always + built as part of Open vSwitch. +

+
+ +
tcp:host[:port]
+
+

+ The specified TCP port on the given host, + which can either be a DNS name (if built with unbound library) or + an IP address (IPv4 or IPv6). If host is an IPv6 + address, wrap it in square brackets, e.g. tcp:[::1]:6640. +

+

+ If port is not specified, it defaults to 6640. +

+
+
pssl:[port][:host]
+
+

+ Listens for SSL connections on the specified TCP port. + Specify 0 for port to have the kernel automatically + choose an available port. If host, which can either + be a DNS name (if built with unbound library) or an IP address, + is specified, then connections are restricted to the resolved or + specified local IP address (either IPv4 or IPv6 address). If + host is an IPv6 address, wrap in square brackets, + e.g. pssl:6640:[::1]. If host is not + specified then it listens only on IPv4 (but not IPv6) addresses. + A valid SSL configuration must be provided when this form is used, + this can be specified either via command-line options or the + table. +

+

+ If port is not specified, it defaults to 6640. +

+

+ SSL support is an optional feature that is not always built as + part of Open vSwitch. +

+
+
ptcp:[port][:host]
+
+

+ Listens for connections on the specified TCP port. + Specify 0 for port to have the kernel automatically + choose an available port. If host, which can either + be a DNS name (if built with unbound library) or an IP address, + is specified, then connections are restricted to the resolved or + specified local IP address (either IPv4 or IPv6 address). If + host is an IPv6 address, wrap it in square brackets, + e.g. ptcp:6640:[::1]. If host is not + specified then it listens only on IPv4 addresses. +

+

+ If port is not specified, it defaults to 6640. +

+
+
+

When multiple clients are configured, the + values must be unique. Duplicate values yield + unspecified results.

+
+ +
+ + + + Maximum number of milliseconds to wait between connection attempts. + Default is implementation-specific. + + + + Maximum number of milliseconds of idle time on connection to the client + before sending an inactivity probe message. If Open vSwitch does not + communicate with the client for the specified number of seconds, it + will send a probe. If a response is not received for the same + additional amount of time, Open vSwitch assumes the connection has been + broken and attempts to reconnect. Default is implementation-specific. + A value of 0 disables inactivity probes. + + + + +

+ Key-value pair of is always updated. + Other key-value pairs in the status columns may be updated depends + on the type. +

+ +

+ When specifies a connection method that + listens for inbound connections (e.g. ptcp: or + punix:), both and + may also be updated while the + remaining key-value pairs are omitted. +

+ +

+ On the other hand, when specifies an + outbound connection, all key-value pairs may be updated, except + the above-mentioned two key-value pairs associated with inbound + connection targets. They are omitted. +

+ + + true if currently connected to this client, + false otherwise. + + + + A human-readable description of the last error on the connection + to the manager; i.e. strerror(errno). This key + will exist only if an error has occurred. + + + +

+ The state of the connection to the manager: +

+
+
VOID
+
Connection is disabled.
+ +
BACKOFF
+
Attempting to reconnect at an increasing period.
+ +
CONNECTING
+
Attempting to connect.
+ +
ACTIVE
+
Connected, remote host responsive.
+ +
IDLE
+
Connection is idle. Waiting for response to keep-alive.
+
+

+ These values may change in the future. They are provided only for + human consumption. +

+
+ + + The amount of time since this client last successfully connected + to the database (in seconds). Value is empty if client has never + successfully been connected. + + + + The amount of time since this client last disconnected from the + database (in seconds). Value is empty if client has never + disconnected. + + + + Space-separated list of the names of OVSDB locks that the connection + holds. Omitted if the connection does not hold any locks. + + + + Space-separated list of the names of OVSDB locks that the connection is + currently waiting to acquire. Omitted if the connection is not waiting + for any locks. + + + + Space-separated list of the names of OVSDB locks that the connection + has had stolen by another OVSDB client. Omitted if no locks have been + stolen from this connection. + + + + When specifies a connection method that + listens for inbound connections (e.g. ptcp: or + pssl:) and more than one connection is actually active, + the value is the number of active connections. Otherwise, this + key-value pair is omitted. + + + + When is ptcp: or + pssl:, this is the TCP port on which the OVSDB server is + listening. (This is particularly useful when specifies a port of 0, allowing the kernel to + choose any available port.) + +
+ + + The overall purpose of these columns is described under Common + Columns at the beginning of this document. + + + + +
+ + SSL configuration for ovn-sb database access. + + + Name of a PEM file containing the private key used as the switch's + identity for SSL connections to the controller. + + + + Name of a PEM file containing a certificate, signed by the + certificate authority (CA) used by the controller and manager, + that certifies the switch's private key, identifying a trustworthy + switch. + + + + Name of a PEM file containing the CA certificate used to verify + that the switch is connected to a trustworthy controller. + + + + If set to true, then Open vSwitch will attempt to + obtain the CA certificate from the controller on its first SSL + connection and save it to the named PEM file. If it is successful, + it will immediately drop the connection and reconnect, and from then + on all SSL connections must be authenticated by a certificate signed + by the CA certificate thus obtained. This option exposes the + SSL connection to a man-in-the-middle attack obtaining the initial + CA certificate. It may still be useful for bootstrapping. + + + + List of SSL protocols to be enabled for SSL connections. The default + when this option is omitted is TLSv1,TLSv1.1,TLSv1.2. + + + + List of ciphers (in OpenSSL cipher string format) to be supported + for SSL connections. The default when this option is omitted is + HIGH:!aNULL:!MD5. + + + + The overall purpose of these columns is described under Common + Columns at the beginning of this document. + + + +
+ +
diff --git a/utilities/.gitignore b/utilities/.gitignore index 5321110..a8ff6e5 100644 --- a/utilities/.gitignore +++ b/utilities/.gitignore @@ -5,6 +5,8 @@ /ovn-sbctl.8 /ovn-inbctl /ovn-inbctl.8 +/ovn-isbctl +/ovn-isbctl.8 /ovn-appctl /ovn-appctl.8 /ovn-trace diff --git a/utilities/automake.mk b/utilities/automake.mk index 565ce18..efa4324 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -71,6 +71,11 @@ bin_PROGRAMS += utilities/ovn-inbctl utilities_ovn_inbctl_SOURCES = utilities/ovn-inbctl.c utilities_ovn_inbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la +# ovn-isbctl +bin_PROGRAMS += utilities/ovn-isbctl +utilities_ovn_isbctl_SOURCES = utilities/ovn-isbctl.c +utilities_ovn_isbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la + # ovn-trace bin_PROGRAMS += utilities/ovn-trace utilities_ovn_trace_SOURCES = utilities/ovn-trace.c diff --git a/utilities/ovn-isbctl.c b/utilities/ovn-isbctl.c new file mode 100644 index 0000000..a6380e8 --- /dev/null +++ b/utilities/ovn-isbctl.c @@ -0,0 +1,890 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "colors.h" +#include "command-line.h" +#include "compiler.h" +#include "db-ctl-base.h" +#include "dirs.h" +#include "fatal-signal.h" +#include "openvswitch/dynamic-string.h" +#include "openvswitch/json.h" +#include "openvswitch/ofp-actions.h" +#include "openvswitch/ofp-flow.h" +#include "openvswitch/ofp-print.h" +#include "openvswitch/shash.h" +#include "openvswitch/vconn.h" +#include "openvswitch/vlog.h" +#include "lib/ovn-isb-idl.h" +#include "lib/ovn-util.h" +#include "ovsdb-data.h" +#include "ovsdb-idl.h" +#include "openvswitch/poll-loop.h" +#include "process.h" +#include "sset.h" +#include "stream-ssl.h" +#include "stream.h" +#include "table.h" +#include "timeval.h" +#include "util.h" +#include "svec.h" + +VLOG_DEFINE_THIS_MODULE(isbctl); + +struct isbctl_context; + +/* --db: The database server to contact. */ +static const char *db; + +/* --oneline: Write each command's output as a single line? */ +static bool oneline; + +/* --dry-run: Do not commit any changes. */ +static bool dry_run; + +/* --timeout: Time to wait for a connection to 'db'. */ +static unsigned int timeout; + +/* Format for table output. */ +static struct table_style table_style = TABLE_STYLE_DEFAULT; + +/* The IDL we're using and the current transaction, if any. + * This is for use by isbctl_exit() only, to allow it to clean up. + * Other code should use its context arguments. */ +static struct ovsdb_idl *the_idl; +static struct ovsdb_idl_txn *the_idl_txn; +OVS_NO_RETURN static void isbctl_exit(int status); + +/* --leader-only, --no-leader-only: Only accept the leader in a cluster. */ +static int leader_only = true; + +static void isbctl_cmd_init(void); +OVS_NO_RETURN static void usage(void); +static void parse_options(int argc, char *argv[], struct shash *local_options); +static void run_prerequisites(struct ctl_command[], size_t n_commands, + struct ovsdb_idl *); +static bool do_isbctl(const char *args, struct ctl_command *, size_t n, + struct ovsdb_idl *); + +int +main(int argc, char *argv[]) +{ + struct ovsdb_idl *idl; + struct ctl_command *commands; + struct shash local_options; + unsigned int seqno; + size_t n_commands; + + set_program_name(argv[0]); + fatal_ignore_sigpipe(); + vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); + vlog_set_levels_from_string_assert("reconnect:warn"); + + isbctl_cmd_init(); + + /* Parse command line. */ + char *args = process_escape_args(argv); + shash_init(&local_options); + parse_options(argc, argv, &local_options); + char *error = ctl_parse_commands(argc - optind, argv + optind, + &local_options, &commands, &n_commands); + if (error) { + ctl_fatal("%s", error); + } + VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, + "Called as %s", args); + + ctl_timeout_setup(timeout); + + /* Initialize IDL. */ + idl = the_idl = ovsdb_idl_create(db, &isbrec_idl_class, false, true); + ovsdb_idl_set_leader_only(idl, leader_only); + run_prerequisites(commands, n_commands, idl); + + /* Execute the commands. + * + * 'seqno' is the database sequence number for which we last tried to + * execute our transaction. There's no point in trying to commit more than + * once for any given sequence number, because if the transaction fails + * it's because the database changed and we need to obtain an up-to-date + * view of the database before we try the transaction again. */ + seqno = ovsdb_idl_get_seqno(idl); + for (;;) { + ovsdb_idl_run(idl); + if (!ovsdb_idl_is_alive(idl)) { + int retval = ovsdb_idl_get_last_error(idl); + ctl_fatal("%s: database connection failed (%s)", + db, ovs_retval_to_string(retval)); + } + + if (seqno != ovsdb_idl_get_seqno(idl)) { + seqno = ovsdb_idl_get_seqno(idl); + if (do_isbctl(args, commands, n_commands, idl)) { + free(args); + exit(EXIT_SUCCESS); + } + } + + if (seqno == ovsdb_idl_get_seqno(idl)) { + ovsdb_idl_wait(idl); + poll_block(); + } + } +} + +static void +parse_options(int argc, char *argv[], struct shash *local_options) +{ + enum { + OPT_DB = UCHAR_MAX + 1, + OPT_ONELINE, + OPT_NO_SYSLOG, + OPT_DRY_RUN, + OPT_LOCAL, + OPT_COMMANDS, + OPT_OPTIONS, + OPT_BOOTSTRAP_CA_CERT, + VLOG_OPTION_ENUMS, + TABLE_OPTION_ENUMS, + SSL_OPTION_ENUMS, + }; + static const struct option global_long_options[] = { + {"db", required_argument, NULL, OPT_DB}, + {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, + {"dry-run", no_argument, NULL, OPT_DRY_RUN}, + {"oneline", no_argument, NULL, OPT_ONELINE}, + {"timeout", required_argument, NULL, 't'}, + {"help", no_argument, NULL, 'h'}, + {"commands", no_argument, NULL, OPT_COMMANDS}, + {"options", no_argument, NULL, OPT_OPTIONS}, + {"leader-only", no_argument, &leader_only, true}, + {"no-leader-only", no_argument, &leader_only, false}, + {"version", no_argument, NULL, 'V'}, + VLOG_LONG_OPTIONS, + STREAM_SSL_LONG_OPTIONS, + {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, + TABLE_LONG_OPTIONS, + {NULL, 0, NULL, 0}, + }; + const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; + char *tmp, *short_options; + + struct option *options; + size_t allocated_options; + size_t n_options; + size_t i; + + tmp = ovs_cmdl_long_options_to_short_options(global_long_options); + short_options = xasprintf("+%s", tmp); + free(tmp); + + /* We want to parse both global and command-specific options here, but + * getopt_long() isn't too convenient for the job. We copy our global + * options into a dynamic array, then append all of the command-specific + * options. */ + options = xmemdup(global_long_options, sizeof global_long_options); + allocated_options = ARRAY_SIZE(global_long_options); + n_options = n_global_long_options; + ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); + + for (;;) { + int idx; + int c; + + c = getopt_long(argc, argv, short_options, options, &idx); + if (c == -1) { + break; + } + + switch (c) { + case OPT_DB: + db = optarg; + break; + + case OPT_ONELINE: + oneline = true; + break; + + case OPT_NO_SYSLOG: + vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); + break; + + case OPT_DRY_RUN: + dry_run = true; + break; + + case OPT_LOCAL: + if (shash_find(local_options, options[idx].name)) { + ctl_fatal("'%s' option specified multiple times", + options[idx].name); + } + shash_add_nocopy(local_options, + xasprintf("--%s", options[idx].name), + nullable_xstrdup(optarg)); + break; + + case 'h': + usage(); + + case OPT_COMMANDS: + ctl_print_commands(); + /* fall through */ + + case OPT_OPTIONS: + ctl_print_options(global_long_options); + /* fall through */ + + case 'V': + ovs_print_version(0, 0); + printf("DB Schema %s\n", isbrec_get_db_version()); + exit(EXIT_SUCCESS); + + case 't': + if (!str_to_uint(optarg, 10, &timeout) || !timeout) { + ctl_fatal("value %s on -t or --timeout is invalid", optarg); + } + break; + + VLOG_OPTION_HANDLERS + TABLE_OPTION_HANDLERS(&table_style) + STREAM_SSL_OPTION_HANDLERS + + case OPT_BOOTSTRAP_CA_CERT: + stream_ssl_set_ca_cert_file(optarg, true); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + + case 0: + break; + } + } + free(short_options); + + if (!db) { + db = default_isb_db(); + } + + for (i = n_global_long_options; options[i].name; i++) { + free(CONST_CAST(char *, options[i].name)); + } + free(options); +} + +static void +usage(void) +{ + printf("\ +%s: OVN interconnection southbound DB management utility\n\ +\n\ +usage: %s [OPTIONS] COMMAND [ARG...]\n\ +\n\ +General commands:\n\ + show print overview of database contents\n\ +\n\ +Connection commands:\n\ + get-connection print the connections\n\ + del-connection delete the connections\n\ + [--inactivity-probe=MSECS]\n\ + set-connection TARGET... set the list of connections to TARGET...\n\ +\n\ +SSL commands:\n\ + get-ssl print the SSL configuration\n\ + del-ssl delete the SSL configuration\n\ + set-ssl PRIV-KEY CERT CA-CERT [SSL-PROTOS [SSL-CIPHERS]] \ +set the SSL configuration\n\ +\n\ +%s\ +%s\ +\n\ +Options:\n\ + --db=DATABASE connect to DATABASE\n\ + (default: %s)\n\ + --no-leader-only accept any cluster member, not just the leader\n\ + -t, --timeout=SECS wait at most SECS seconds\n\ + --dry-run do not commit changes to database\n\ + --oneline print exactly one line of output per command\n", + program_name, program_name, ctl_get_db_cmd_usage(), + ctl_list_db_tables_usage(), default_isb_db()); + table_usage(); + vlog_usage(); + printf("\ + --no-syslog equivalent to --verbose=isbctl:syslog:warn\n"); + printf("\n\ +Other options:\n\ + -h, --help display this help message\n\ + -V, --version display version information\n"); + stream_usage("database", true, true, true); + exit(EXIT_SUCCESS); +} + + +/* ovs-isbctl specific context. Inherits the 'struct ctl_context' as base. */ +struct isbctl_context { + struct ctl_context base; + + /* A cache of the contents of the database. + * + * A command that needs to use any of this information must first call + * isbctl_context_populate_cache(). A command that changes anything that + * could invalidate the cache must either call + * isbctl_context_invalidate_cache() or manually update the cache to + * maintain its correctness. */ + bool cache_valid; +}; + +/* Casts 'base' into 'struct isbctl_context'. */ +static struct isbctl_context * +isbctl_context_cast(struct ctl_context *base) +{ + return CONTAINER_OF(base, struct isbctl_context, base); +} + +static void +isbctl_context_invalidate_cache(struct ctl_context *ctx) +{ + struct isbctl_context *isbctl_ctx = isbctl_context_cast(ctx); + + if (!isbctl_ctx->cache_valid) { + return; + } + isbctl_ctx->cache_valid = false; +} + +OVS_UNUSED static void +isbctl_context_populate_cache(struct ctl_context *ctx) +{ + struct isbctl_context *isbctl_ctx = isbctl_context_cast(ctx); + + if (isbctl_ctx->cache_valid) { + /* Cache is already populated. */ + return; + } + isbctl_ctx->cache_valid = true; +} + +OVS_UNUSED static void +pre_get_info(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &isbrec_availability_zone_col_name); +} + +static struct cmd_show_table cmd_show_tables[] = { + {&isbrec_table_availability_zone, + &isbrec_availability_zone_col_name, + {NULL}, + {NULL, NULL, NULL}}, + + {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}}, +}; + +static void +isbctl_init(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static void +verify_connections(struct ctl_context *ctx) +{ + const struct isbrec_isb_global *isb_global = + isbrec_isb_global_first(ctx->idl); + const struct isbrec_connection *conn; + + isbrec_isb_global_verify_connections(isb_global); + + ISBREC_CONNECTION_FOR_EACH(conn, ctx->idl) { + isbrec_connection_verify_target(conn); + } +} + +static void +pre_connection(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &isbrec_isb_global_col_connections); + ovsdb_idl_add_column(ctx->idl, &isbrec_connection_col_target); + ovsdb_idl_add_column(ctx->idl, &isbrec_connection_col_inactivity_probe); +} + +static void +cmd_get_connection(struct ctl_context *ctx) +{ + const struct isbrec_connection *conn; + struct svec targets; + size_t i; + + verify_connections(ctx); + + /* Print the targets in sorted order for reproducibility. */ + svec_init(&targets); + + ISBREC_CONNECTION_FOR_EACH(conn, ctx->idl) { + svec_add(&targets, conn->target); + } + + svec_sort_unique(&targets); + for (i = 0; i < targets.n; i++) { + ds_put_format(&ctx->output, "%s\n", targets.names[i]); + } + svec_destroy(&targets); +} + +static void +delete_connections(struct ctl_context *ctx) +{ + const struct isbrec_isb_global *isb_global = + isbrec_isb_global_first(ctx->idl); + const struct isbrec_connection *conn, *next; + + /* Delete Manager rows pointed to by 'connection_options' column. */ + ISBREC_CONNECTION_FOR_EACH_SAFE(conn, next, ctx->idl) { + isbrec_connection_delete(conn); + } + + /* Delete 'Manager' row refs in 'manager_options' column. */ + isbrec_isb_global_set_connections(isb_global, NULL, 0); +} + +static void +cmd_del_connection(struct ctl_context *ctx) +{ + verify_connections(ctx); + delete_connections(ctx); +} + +static void +insert_connections(struct ctl_context *ctx, char *targets[], size_t n) +{ + const struct isbrec_isb_global *isb_global = + isbrec_isb_global_first(ctx->idl); + struct isbrec_connection **connections; + size_t i, conns=0; + const char *inactivity_probe = shash_find_data(&ctx->options, + "--inactivity-probe"); + + /* Insert each connection in a new row in Connection table. */ + connections = xmalloc(n * sizeof *connections); + for (i = 0; i < n; i++) { + if (stream_verify_name(targets[i]) && + pstream_verify_name(targets[i])) { + VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); + } + + connections[conns] = isbrec_connection_insert(ctx->txn); + isbrec_connection_set_target(connections[conns], targets[i]); + if (inactivity_probe) { + int64_t msecs = atoll(inactivity_probe); + isbrec_connection_set_inactivity_probe(connections[conns], + &msecs, 1); + } + conns++; + } + + /* Store uuids of new connection rows in 'connection' column. */ + isbrec_isb_global_set_connections(isb_global, connections, conns); + free(connections); +} + +static void +cmd_set_connection(struct ctl_context *ctx) +{ + const size_t n = ctx->argc - 1; + + verify_connections(ctx); + delete_connections(ctx); + insert_connections(ctx, &ctx->argv[1], n); +} + +static void +pre_cmd_get_ssl(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &isbrec_isb_global_col_ssl); + + ovsdb_idl_add_column(ctx->idl, &isbrec_ssl_col_private_key); + ovsdb_idl_add_column(ctx->idl, &isbrec_ssl_col_certificate); + ovsdb_idl_add_column(ctx->idl, &isbrec_ssl_col_ca_cert); + ovsdb_idl_add_column(ctx->idl, &isbrec_ssl_col_bootstrap_ca_cert); +} + +static void +cmd_get_ssl(struct ctl_context *ctx) +{ + const struct isbrec_isb_global *isb_global = + isbrec_isb_global_first(ctx->idl); + const struct isbrec_ssl *ssl = isbrec_ssl_first(ctx->idl); + + isbrec_isb_global_verify_ssl(isb_global); + if (ssl) { + isbrec_ssl_verify_private_key(ssl); + isbrec_ssl_verify_certificate(ssl); + isbrec_ssl_verify_ca_cert(ssl); + isbrec_ssl_verify_bootstrap_ca_cert(ssl); + + ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key); + ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate); + ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert); + ds_put_format(&ctx->output, "Bootstrap: %s\n", + ssl->bootstrap_ca_cert ? "true" : "false"); + } +} + +static void +pre_cmd_del_ssl(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &isbrec_isb_global_col_ssl); +} + +static void +cmd_del_ssl(struct ctl_context *ctx) +{ + const struct isbrec_isb_global *isb_global = + isbrec_isb_global_first(ctx->idl); + const struct isbrec_ssl *ssl = isbrec_ssl_first(ctx->idl); + + if (ssl) { + isbrec_isb_global_verify_ssl(isb_global); + isbrec_ssl_delete(ssl); + isbrec_isb_global_set_ssl(isb_global, NULL); + } +} + +static void +pre_cmd_set_ssl(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &isbrec_isb_global_col_ssl); +} + +static void +cmd_set_ssl(struct ctl_context *ctx) +{ + bool bootstrap = shash_find(&ctx->options, "--bootstrap"); + const struct isbrec_isb_global *isb_global = + isbrec_isb_global_first(ctx->idl); + const struct isbrec_ssl *ssl = isbrec_ssl_first(ctx->idl); + + isbrec_isb_global_verify_ssl(isb_global); + if (ssl) { + isbrec_ssl_delete(ssl); + } + ssl = isbrec_ssl_insert(ctx->txn); + + isbrec_ssl_set_private_key(ssl, ctx->argv[1]); + isbrec_ssl_set_certificate(ssl, ctx->argv[2]); + isbrec_ssl_set_ca_cert(ssl, ctx->argv[3]); + + isbrec_ssl_set_bootstrap_ca_cert(ssl, bootstrap); + + if (ctx->argc == 5) { + isbrec_ssl_set_ssl_protocols(ssl, ctx->argv[4]); + } else if (ctx->argc == 6) { + isbrec_ssl_set_ssl_protocols(ssl, ctx->argv[4]); + isbrec_ssl_set_ssl_ciphers(ssl, ctx->argv[5]); + } + + isbrec_isb_global_set_ssl(isb_global, ssl); +} + + +static const struct ctl_table_class tables[ISBREC_N_TABLES] = { + [ISBREC_TABLE_AVAILABILITY_ZONE].row_ids[0] = + {&isbrec_availability_zone_col_name, NULL, NULL}, +}; + + +static void +isbctl_context_init_command(struct isbctl_context *isbctl_ctx, + struct ctl_command *command) +{ + ctl_context_init_command(&isbctl_ctx->base, command); +} + +static void +isbctl_context_init(struct isbctl_context *isbctl_ctx, + struct ctl_command *command, struct ovsdb_idl *idl, + struct ovsdb_idl_txn *txn, + struct ovsdb_symbol_table *symtab) +{ + ctl_context_init(&isbctl_ctx->base, command, idl, txn, symtab, + isbctl_context_invalidate_cache); + isbctl_ctx->cache_valid = false; +} + +static void +isbctl_context_done_command(struct isbctl_context *isbctl_ctx, + struct ctl_command *command) +{ + ctl_context_done_command(&isbctl_ctx->base, command); +} + +static void +isbctl_context_done(struct isbctl_context *isbctl_ctx, + struct ctl_command *command) +{ + ctl_context_done(&isbctl_ctx->base, command); +} + +static void +run_prerequisites(struct ctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl) +{ + ovsdb_idl_add_table(idl, &isbrec_table_isb_global); + + for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { + if (c->syntax->prerequisites) { + struct isbctl_context isbctl_ctx; + + ds_init(&c->output); + c->table = NULL; + + isbctl_context_init(&isbctl_ctx, c, idl, NULL, NULL); + (c->syntax->prerequisites)(&isbctl_ctx.base); + if (isbctl_ctx.base.error) { + ctl_fatal("%s", isbctl_ctx.base.error); + } + isbctl_context_done(&isbctl_ctx, c); + + ovs_assert(!c->output.string); + ovs_assert(!c->table); + } + } +} + +static bool +do_isbctl(const char *args, struct ctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl) +{ + struct ovsdb_idl_txn *txn; + enum ovsdb_idl_txn_status status; + struct ovsdb_symbol_table *symtab; + struct isbctl_context isbctl_ctx; + struct ctl_command *c; + struct shash_node *node; + + txn = the_idl_txn = ovsdb_idl_txn_create(idl); + if (dry_run) { + ovsdb_idl_txn_set_dry_run(txn); + } + + ovsdb_idl_txn_add_comment(txn, "ovs-isbctl: %s", args); + + const struct isbrec_isb_global *isb = isbrec_isb_global_first(idl); + if (!isb) { + /* XXX add verification that table is empty */ + isbrec_isb_global_insert(txn); + } + + symtab = ovsdb_symbol_table_create(); + for (c = commands; c < &commands[n_commands]; c++) { + ds_init(&c->output); + c->table = NULL; + } + isbctl_context_init(&isbctl_ctx, NULL, idl, txn, symtab); + for (c = commands; c < &commands[n_commands]; c++) { + isbctl_context_init_command(&isbctl_ctx, c); + if (c->syntax->run) { + (c->syntax->run)(&isbctl_ctx.base); + } + if (isbctl_ctx.base.error) { + ctl_fatal("%s", isbctl_ctx.base.error); + } + isbctl_context_done_command(&isbctl_ctx, c); + + if (isbctl_ctx.base.try_again) { + isbctl_context_done(&isbctl_ctx, NULL); + goto try_again; + } + } + isbctl_context_done(&isbctl_ctx, NULL); + + SHASH_FOR_EACH (node, &symtab->sh) { + struct ovsdb_symbol *symbol = node->data; + if (!symbol->created) { + ctl_fatal("row id \"%s\" is referenced but never created (e.g. " + "with \"-- --id=%s create ...\")", + node->name, node->name); + } + if (!symbol->strong_ref) { + if (!symbol->weak_ref) { + VLOG_WARN("row id \"%s\" was created but no reference to it " + "was inserted, so it will not actually appear in " + "the database", node->name); + } else { + VLOG_WARN("row id \"%s\" was created but only a weak " + "reference to it was inserted, so it will not " + "actually appear in the database", node->name); + } + } + } + + status = ovsdb_idl_txn_commit_block(txn); + if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { + for (c = commands; c < &commands[n_commands]; c++) { + if (c->syntax->postprocess) { + isbctl_context_init(&isbctl_ctx, c, idl, txn, symtab); + (c->syntax->postprocess)(&isbctl_ctx.base); + if (isbctl_ctx.base.error) { + ctl_fatal("%s", isbctl_ctx.base.error); + } + isbctl_context_done(&isbctl_ctx, c); + } + } + } + + switch (status) { + case TXN_UNCOMMITTED: + case TXN_INCOMPLETE: + OVS_NOT_REACHED(); + + case TXN_ABORTED: + /* Should not happen--we never call ovsdb_idl_txn_abort(). */ + ctl_fatal("transaction aborted"); + + case TXN_UNCHANGED: + case TXN_SUCCESS: + break; + + case TXN_TRY_AGAIN: + goto try_again; + + case TXN_ERROR: + ctl_fatal("transaction error: %s", ovsdb_idl_txn_get_error(txn)); + + case TXN_NOT_LOCKED: + /* Should not happen--we never call ovsdb_idl_set_lock(). */ + ctl_fatal("database not locked"); + + default: + OVS_NOT_REACHED(); + } + + ovsdb_symbol_table_destroy(symtab); + + for (c = commands; c < &commands[n_commands]; c++) { + struct ds *ds = &c->output; + + if (c->table) { + table_print(c->table, &table_style); + } else if (oneline) { + size_t j; + + ds_chomp(ds, '\n'); + for (j = 0; j < ds->length; j++) { + int ch = ds->string[j]; + switch (ch) { + case '\n': + fputs("\\n", stdout); + break; + + case '\\': + fputs("\\\\", stdout); + break; + + default: + putchar(ch); + } + } + putchar('\n'); + } else { + fputs(ds_cstr(ds), stdout); + } + ds_destroy(&c->output); + table_destroy(c->table); + free(c->table); + + shash_destroy_free_data(&c->options); + } + free(commands); + ovsdb_idl_txn_destroy(txn); + ovsdb_idl_destroy(idl); + + return true; + +try_again: + /* Our transaction needs to be rerun, or a prerequisite was not met. Free + * resources and return so that the caller can try again. */ + ovsdb_idl_txn_abort(txn); + ovsdb_idl_txn_destroy(txn); + the_idl_txn = NULL; + + ovsdb_symbol_table_destroy(symtab); + for (c = commands; c < &commands[n_commands]; c++) { + ds_destroy(&c->output); + table_destroy(c->table); + free(c->table); + } + return false; +} + +/* Frees the current transaction and the underlying IDL and then calls + * exit(status). + * + * Freeing the transaction and the IDL is not strictly necessary, but it makes + * for a clean memory leak report from valgrind in the normal case. That makes + * it easier to notice real memory leaks. */ +static void +isbctl_exit(int status) +{ + if (the_idl_txn) { + ovsdb_idl_txn_abort(the_idl_txn); + ovsdb_idl_txn_destroy(the_idl_txn); + } + ovsdb_idl_destroy(the_idl); + exit(status); +} + +static const struct ctl_command_syntax isbctl_commands[] = { + { "init", 0, 0, "", NULL, isbctl_init, NULL, "", RW }, + + /* Connection commands. */ + {"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "", + RO}, + {"del-connection", 0, 0, "", pre_connection, cmd_del_connection, NULL, "", + RW}, + {"set-connection", 1, INT_MAX, "TARGET...", pre_connection, + cmd_set_connection, NULL, "--inactivity-probe=", RW}, + + /* SSL commands. */ + {"get-ssl", 0, 0, "", pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO}, + {"del-ssl", 0, 0, "", pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW}, + {"set-ssl", 3, 5, + "PRIVATE-KEY CERTIFICATE CA-CERT [SSL-PROTOS [SSL-CIPHERS]]", + pre_cmd_set_ssl, cmd_set_ssl, NULL, "--bootstrap", RW}, + + {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, +}; + +/* Registers isbctl and common db commands. */ +static void +isbctl_cmd_init(void) +{ + ctl_init(&isbrec_idl_class, isbrec_table_classes, tables, + cmd_show_tables, isbctl_exit); + ctl_register_commands(isbctl_commands); +}