Message ID | 1572469973-2733-4-git-send-email-hzhou@ovn.org |
---|---|
State | Superseded |
Headers | show |
Series | OVN Interconnection | expand |
Bleep bloop. Greetings Han Zhou, I am a robot and I have tried out your patch. Thanks for your contribution. I encountered some error that I wasn't expecting. See the details below. checkpatch: WARNING: Line is 80 characters long (recommended limit is 79) #271 FILE: ovn-isb.ovsschema:34: "refTable": "Availability_Zone"}}}, WARNING: Line is 80 characters long (recommended limit is 79) #310 FILE: ovn-isb.ovsschema:73: "refTable": "Availability_Zone"}}}, WARNING: Line is 82 characters long (recommended limit is 79) #402 FILE: ovn-isb.xml:30: These tables contain objects that are availability zone specific. Each object WARNING: Line is 83 characters long (recommended limit is 79) #403 FILE: ovn-isb.xml:31: is owned and populated by one availability zone, and read by other availability WARNING: Line is 82 characters long (recommended limit is 79) #468 FILE: ovn-isb.xml:96: Each row in this table represents an Availability Zone. Each OVN deployment WARNING: Line is 81 characters long (recommended limit is 79) #469 FILE: ovn-isb.xml:97: is considered an availability zone from OVN control plane perspective, with WARNING: Line is 81 characters long (recommended limit is 79) #470 FILE: ovn-isb.xml:98: its own central components, such as northbound and southbound databases and WARNING: Line is 81 characters long (recommended limit is 79) #578 FILE: ovn-isb.xml:206: see <ref table="Datapath_Binding" column="tunnel_key"/> column of the OVN WARNING: Line is 81 characters long (recommended limit is 79) #579 FILE: ovn-isb.xml:207: Southbound database's <ref table="Datapath_Binding" db="OVN_Southbound"/> WARNING: Line is 81 characters long (recommended limit is 79) #647 FILE: ovn-isb.xml:275: The Ethernet address and IP addresses used by the corresponding logical WARNING: Line is 84 characters long (recommended limit is 79) #648 FILE: ovn-isb.xml:276: router port peering with the transit switch port. It is a string combined WARNING: Line is 82 characters long (recommended limit is 79) #691 FILE: ovn-isb.xml:319: <dt><code>ssl:<var>host</var></code>[<code>:<var>port</var></code>]</dt> WARNING: Line is 82 characters long (recommended limit is 79) #709 FILE: ovn-isb.xml:337: <dt><code>tcp:<var>host</var></code>[<code>:<var>port</var></code>]</dt> WARNING: Line is 84 characters long (recommended limit is 79) #715 FILE: ovn-isb.xml:343: address, wrap it in square brackets, e.g. <code>tcp:[::1]:6640</code>. WARNING: Line is 85 characters long (recommended limit is 79) #721 FILE: ovn-isb.xml:349: <dt><code>pssl:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> WARNING: Line is 80 characters long (recommended limit is 79) #733 FILE: ovn-isb.xml:361: A valid SSL configuration must be provided when this form is used, WARNING: Line is 85 characters long (recommended limit is 79) #745 FILE: ovn-isb.xml:373: <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> WARNING: Line is 110 characters long (recommended limit is 79) #821 FILE: ovn-isb.xml:449: type='{"type": "string", "enum": ["set", ["VOID", "BACKOFF", "CONNECTING", "ACTIVE", "IDLE"]]}'> WARNING: Line is 94 characters long (recommended limit is 79) #1167 FILE: utilities/ovn-isbctl.8.xml:4: <p>ovn-isbctl -- Open Virtual Network interconnection southbound db management utility</p> WARNING: Line is 94 characters long (recommended limit is 79) #1170 FILE: utilities/ovn-isbctl.8.xml:7: <p><code>ovn-isbctl</code> [<var>options</var>] <var>command</var> [<var>arg</var>...]</p> WARNING: Line is 90 characters long (recommended limit is 79) #1173 FILE: utilities/ovn-isbctl.8.xml:10: <p>This utility can be used to manage the OVN interconnection southbound database.</p> WARNING: Line is 81 characters long (recommended limit is 79) #1193 FILE: utilities/ovn-isbctl.8.xml:30: <p>These commands query and modify the contents of <code>ovsdb</code> tables. WARNING: Line is 90 characters long (recommended limit is 79) #1195 FILE: utilities/ovn-isbctl.8.xml:32: as such they operate at a lower level than other <code>ovn-isbctl</code> commands.</p> WARNING: Line is 82 characters long (recommended limit is 79) #1197 FILE: utilities/ovn-isbctl.8.xml:34: <p>Each of these commands has a <var>table</var> parameter to identify a table WARNING: Line is 87 characters long (recommended limit is 79) #1221 FILE: utilities/ovn-isbctl.8.xml:58: <xi:include href="lib/db-ctl-base.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> WARNING: Line is 114 characters long (recommended limit is 79) #1235 FILE: utilities/ovn-isbctl.8.xml:72: <dt>[<code>--inactivity-probe=</code><var>msecs</var>] <code>set-connection</code> <var>target</var>...</dt> WARNING: Line is 80 characters long (recommended limit is 79) #1238 FILE: utilities/ovn-isbctl.8.xml:75: <code>--inactivity-probe=</code><var>msecs</var> to override the default WARNING: Line is 83 characters long (recommended limit is 79) #1239 FILE: utilities/ovn-isbctl.8.xml:76: idle connection inactivity probe time. Use 0 to disable inactivity probes. WARNING: Line is 83 characters long (recommended limit is 79) #1270 FILE: utilities/ovn-isbctl.8.xml:107: Otherwise, the default is <code>unix:@RUNDIR@/ovnisb_db.sock</code>, but this WARNING: Line is 80 characters long (recommended limit is 79) #1282 FILE: utilities/ovn-isbctl.8.xml:119: <code>--no-leader-only</code>, <code>ovn-isbctl</code> will use any server WARNING: Line is 80 characters long (recommended limit is 79) #1292 FILE: utilities/ovn-isbctl.8.xml:129: <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> WARNING: Line is 81 characters long (recommended limit is 79) #1297 FILE: utilities/ovn-isbctl.8.xml:134: <xi:include href="lib/table.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> WARNING: Line is 89 characters long (recommended limit is 79) #1305 FILE: utilities/ovn-isbctl.8.xml:142: <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> WARNING: Line is 82 characters long (recommended limit is 79) #1309 FILE: utilities/ovn-isbctl.8.xml:146: <xi:include href="lib/common.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> WARNING: Line lacks whitespace around operator #1619 FILE: utilities/ovn-isbctl.c:302: get-connection print the connections\n\ WARNING: Line lacks whitespace around operator #1620 FILE: utilities/ovn-isbctl.c:303: del-connection delete the connections\n\ WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator #1621 FILE: utilities/ovn-isbctl.c:304: [--inactivity-probe=MSECS]\n\ WARNING: Line lacks whitespace around operator #1622 FILE: utilities/ovn-isbctl.c:305: set-connection TARGET... set the list of connections to TARGET...\n\ WARNING: Line lacks whitespace around operator #1625 FILE: utilities/ovn-isbctl.c:308: get-ssl print the SSL configuration\n\ WARNING: Line lacks whitespace around operator #1626 FILE: utilities/ovn-isbctl.c:309: del-ssl delete the SSL configuration\n\ WARNING: Line lacks whitespace around operator #1627 FILE: utilities/ovn-isbctl.c:310: set-ssl PRIV-KEY CERT CA-CERT [SSL-PROTOS [SSL-CIPHERS]] \ WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator #1634 FILE: utilities/ovn-isbctl.c:317: --db=DATABASE connect to DATABASE\n\ WARNING: Line lacks whitespace around operator #1636 FILE: utilities/ovn-isbctl.c:319: --no-leader-only accept any cluster member, not just the leader\n\ WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator #1637 FILE: utilities/ovn-isbctl.c:320: -t, --timeout=SECS wait at most SECS seconds\n\ WARNING: Line lacks whitespace around operator #1638 FILE: utilities/ovn-isbctl.c:321: --dry-run do not commit changes to database\n\ WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator WARNING: Line lacks whitespace around operator #1645 FILE: utilities/ovn-isbctl.c:328: --no-syslog equivalent to --verbose=isbctl:syslog:warn\n"); WARNING: Comment with 'xxx' marker #2143 FILE: utilities/ovn-isbctl.c:826: /* XXX add verification that table is empty */ Lines checked: 2338, Warnings: 53, Errors: 0 Please check this out. If you feel there has been an error, please email aconole@redhat.com Thanks, 0-day Robot
This patch has some whitespace warning when applying the patch. Applying: ovn-isb: Interconnection southbound DB schema and CLI. .git/rebase-apply/patch:171: new blank line at EOF. + warning: 1 line adds whitespace errors. On Thu, Oct 31, 2019 at 2:48 AM Han Zhou <hzhou@ovn.org> wrote: > > This patch introduces OVN_IC_Southbound DB schema and the CLI > ovn-isbctl that manages the DB. > > Signed-off-by: Han Zhou <hzhou@ovn.org> > --- > .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 | 129 ++++++ > ovn-isb.xml | 582 +++++++++++++++++++++++++ > tests/automake.mk | 2 + > tests/ovn-isbctl.at | 112 +++++ > tests/testsuite.at | 1 + > utilities/.gitignore | 2 + > utilities/automake.mk | 8 + > utilities/ovn-isbctl.8.xml | 148 +++++++ > utilities/ovn-isbctl.c | 1015 ++++++++++++++++++++++++++++++++++++++++++++ > 18 files changed, 2085 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 tests/ovn-isbctl.at > create mode 100644 utilities/ovn-isbctl.8.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..3d3b54e > --- /dev/null > +++ b/ovn-isb.ovsschema > @@ -0,0 +1,129 @@ > +{ > + "name": "OVN_IC_Southbound", > + "version": "1.0.0", > + "cksum": "3575687822 6054", > + "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"}}}, > + "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..0024165 > --- /dev/null > +++ b/ovn-isb.xml > @@ -0,0 +1,582 @@ > +<?xml version="1.0" encoding="utf-8"?> > +<database name="ovn-isb" title="OVN Interconnection Southbound Database"> > + <p> > + This database holds configuration and state for interconnecting different > + OVN deployments. The content of the database is populated and used by the > + <code>ovn-ic</code> program in each OVN deployment, and not supposed to be > + directly used by CMS or end user. > + </p> > + > + <p> > + The OVN Interconnection Southbound database is shared by > + <code>ovn-ic</code> 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 <code>ovn-ic</code> program in each deployment is > + responsible for syncing the data between this database and the its own > + northbound and southbound databases. > + </p> > + > + <h2>Database Structure</h2> > + > + <p> > + The OVN Interconnection Southbound database contains classes of data with > + different properties, as described in the sections below. > + </p> > + > + <h3>Availability Zone Specific Information</h3> > + > + <p> > + 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. > + </p> > + > + <p> > + The <ref table="Availability_Zone"/>, <ref table="Gateway"/>, <ref > + table="Encap"/> and <ref table="Port_Binding"/> tables are the availability > + zone specific tables. > + </p> > + > + <h3>Global Information</h3> > + > + <p> > + The data that does not belong to any specific availability zone but is > + common for all availability zones. > + </p> > + > + <p> > + The <ref table="Datapath_Binding"/> table contains the common datapath > + binding information. > + </p> > + > + <h3>Common Columns</h3> > + <p> > + Each of the tables in this database contains a special column, named > + <code>external_ids</code>. This column has the same form and purpose each > + place it appears. > + </p> > + > + <dl> > + <dt><code>external_ids</code>: map of string-string pairs</dt> > + <dd> > + Key-value pairs for use by <code>ovn-ic</code>. > + </dd> > + </dl> > + <table name="ISB_Global" title="IC Southbound configuration"> > + <p> > + Interconnection Southbound configuration. This table must have exactly > + one row. > + </p> > + > + <group title="Common Columns"> > + <column name="external_ids"> > + See <em>External IDs</em> at the beginning of this document. > + </column> > + > + <column name="options"> > + </column> > + </group> > + > + <group title="Connection Options"> > + <column name="connections"> > + 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 <ref table="Connection"/> > + table for more information. > + </column> > + <column name="ssl"> > + Global SSL configuration. > + </column> > + </group> > + </table> > + > + <table name="Availability_Zone" title="Availability Zone Information"> > + <p> > + 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 > + <code>ovn-northd</code> daemon. > + </p> > + > + <column name="name"> > + A name that uniquely identifies the availability zone. > + </column> > + </table> > + > + <table name="Gateway" title="Interconnection Gateway Information"> > + <p> > + Each row in this table represents a interconnection gateway chassis in an > + availability zone. > + </p> > + > + <column name="name"> > + The name of the gateway. See <ref table="Chassis" column="name"/> column > + of the OVN Southbound database's <ref table="Chassis" > + db="OVN_Southbound"/> table. > + </column> > + > + <column name="availability_zone"> > + The availabilty zone that the gateway belongs to. > + </column> > + > + <column name="hostname"> > + The hostname of the gateway. > + </column> > + > + <group title="Common Columns"> > + The overall purpose of these columns is described under <code>Common > + Columns</code> at the beginning of this document. > + > + <column name="external_ids"/> > + </group> > + > + <group title="Encapsulation Configuration"> > + <p> > + OVN uses encapsulation to transmit logical dataplane packets > + between gateways. > + </p> > + > + <column name="encaps"> > + Points to supported encapsulation configurations to transmit > + logical dataplane packets to this gateway. Each entry is a <ref > + table="Encap"/> record that describes the configuration. > + See <ref table="Chassis" column="encaps"/> column > + of the OVN Southbound database's <ref table="Chassis" > + db="OVN_Southbound"/> table. > + </column> > + </group> > + > + </table> > + > + <table name="Encap" title="Encapsulation Types"> > + <p> > + The <ref column="encaps" table="Gateway"/> column in the <ref > + table="Gateway"/> table refers to rows in this table to identify > + how OVN may transmit logical dataplane packets to this gateway. > + </p> > + > + <column name="type"> > + The encapsulation to use to transmit packets to this gateway. > + See <ref table="Encap" column="type"/> column > + of the OVN Southbound database's <ref table="Encap" > + db="OVN_Southbound"/> table. > + </column> > + > + <column name="options"> > + Options for configuring the encapsulation, which may be <ref > + column="type"/> specific. See <ref table="Encap" column="options"/> > + column of the OVN Southbound database's <ref table="Encap" > + db="OVN_Southbound"/> table. > + </column> > + > + <column name="ip"> > + The IPv4 address of the encapsulation tunnel endpoint. > + </column> > + > + <column name="gateway_name"> > + The name of the gateway that created this encap. > + </column> > + </table> > + > + <table name="Datapath_Binding" title="Transit Switch Datapath Bindings"> > + <p> > + Each row in this table represents a logical datapath for a transit > + logical switch configured in the OVN Interconnection Northbound > + database's <ref table="Transit_Switch" db="OVN_IC_Northbound"/> table. > + </p> > + > + <column name="transit_switch"> > + The name of the transit logical switch that is configured in the OVN > + Interconnection Northbound database's <ref table="Transit_Switch" > + db="OVN_IC_Northbound"/> table. > + </column> > + > + <column name="tunnel_key"> > + <p> > + The tunnel key value to which the logical datapath is bound. The key > + can be generated by any <code>ovn-ic</code> 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. > + </p> > + > + <p> > + For more information about the meanings of a tunnel key, > + see <ref table="Datapath_Binding" column="tunnel_key"/> column of the OVN > + Southbound database's <ref table="Datapath_Binding" db="OVN_Southbound"/> > + table. > + </p> > + </column> > + > + <group title="Common Columns"> > + The overall purpose of these columns is described under <code>Common > + Columns</code> at the beginning of this document. > + > + <column name="external_ids"/> > + </group> > + </table> > + > + <table name="Port_Binding" title="Transit Port Bindings"> > + <p> > + 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. > + </p> > + > + <group title="Core Features"> > + <column name="transit_switch"> > + The name of the transit switch that the corresponding logical port > + belongs to. > + </column> > + > + <column name="logical_port"> > + A logical port, taken from <ref table="Logical_Switch_Port" > + column="name" db="OVN_Northbound"/> in the OVN_Northbound > + database's <ref table="Logical_Switch_Port" db="OVN_Northbound"/> > + table. The logical port name must be unique across all availability > + zones. > + </column> > + > + <column name="availability_zone"> > + The availability zone that the port belongs to. > + </column> > + > + <column name="encap"> > + Points to supported encapsulation configurations to transmit > + logical dataplane packets to this gateway. Each entry is a <ref > + table="Encap"/> record that describes the configuration. > + </column> > + > + <column name="gateway"> > + The name of the gateway that this port is physically located. > + </column> > + > + <column name="tunnel_key"> > + <p> > + 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 <code>ovn-ic</code> but the same key is > + shared by all availability zones so that the packets can go through > + the datapath pipelines of different availability zones. > + </p> > + <p> > + The tunnel ID must be unique within the scope of a logical datapath. > + </p> > + <p> > + For more information about tunnel key, see <ref table="Port_Binding" > + column="tunnel_key"/> column of the OVN Southbound database's <ref > + table="Port_Binding" db="OVN_Southbound"/> table. > + </p> > + </column> > + > + <column name="address"> > + <p> > + 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 <ref table="Logical_Router_Port" column="mac"/> > + column followed by the values in <ref table="Logical_Router_Port" > + column="networks"/> column in <ref table="Logical_Router_Port" > + db="OVN_Northbound"/> table. > + </p> > + </column> > + > + </group> > + > + <group title="Common Columns"> > + <column name="external_ids"> > + <p> > + See <em>External IDs</em> at the beginning of this document. > + </p> > + </column> > + </group> > + </table> > + > + <table name="Connection" title="OVSDB client connections."> > + <p> > + Configuration for a database connection to an Open vSwitch database > + (OVSDB) client. > + </p> > + > + <p> > + This table primarily configures the Open vSwitch database server > + (<code>ovsdb-server</code>). > + </p> > + > + <p> > + The Open vSwitch database server can initiate and maintain active > + connections to remote clients. It can also listen for database > + connections. > + </p> > + > + <group title="Core Features"> > + <column name="target"> > + <p>Connection methods for clients.</p> > + <p> > + The following connection methods are currently supported: > + </p> > + <dl> > + <dt><code>ssl:<var>host</var></code>[<code>:<var>port</var></code>]</dt> > + <dd> > + <p> > + The specified SSL <var>port</var> on the given <var>host</var>, > + 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 <ref table="SSL"/> table. > + </p> > + <p> > + If <var>port</var> is not specified, it defaults to 6640. > + </p> > + <p> > + SSL support is an optional feature that is not always > + built as part of Open vSwitch. > + </p> > + </dd> > + > + <dt><code>tcp:<var>host</var></code>[<code>:<var>port</var></code>]</dt> > + <dd> > + <p> > + The specified TCP <var>port</var> on the given <var>host</var>, > + which can either be a DNS name (if built with unbound library) or > + an IP address (IPv4 or IPv6). If <var>host</var> is an IPv6 > + address, wrap it in square brackets, e.g. <code>tcp:[::1]:6640</code>. > + </p> > + <p> > + If <var>port</var> is not specified, it defaults to 6640. > + </p> > + </dd> > + <dt><code>pssl:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> > + <dd> > + <p> > + Listens for SSL connections on the specified TCP <var>port</var>. > + Specify 0 for <var>port</var> to have the kernel automatically > + choose an available port. If <var>host</var>, 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 > + <var>host</var> is an IPv6 address, wrap in square brackets, > + e.g. <code>pssl:6640:[::1]</code>. If <var>host</var> 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 > + <ref table="SSL"/> table. > + </p> > + <p> > + If <var>port</var> is not specified, it defaults to 6640. > + </p> > + <p> > + SSL support is an optional feature that is not always built as > + part of Open vSwitch. > + </p> > + </dd> > + <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> > + <dd> > + <p> > + Listens for connections on the specified TCP <var>port</var>. > + Specify 0 for <var>port</var> to have the kernel automatically > + choose an available port. If <var>host</var>, 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 > + <var>host</var> is an IPv6 address, wrap it in square brackets, > + e.g. <code>ptcp:6640:[::1]</code>. If <var>host</var> is not > + specified then it listens only on IPv4 addresses. > + </p> > + <p> > + If <var>port</var> is not specified, it defaults to 6640. > + </p> > + </dd> > + </dl> > + <p>When multiple clients are configured, the <ref column="target"/> > + values must be unique. Duplicate <ref column="target"/> values yield > + unspecified results.</p> > + </column> > + > + </group> > + > + <group title="Client Failure Detection and Handling"> > + <column name="max_backoff"> > + Maximum number of milliseconds to wait between connection attempts. > + Default is implementation-specific. > + </column> > + > + <column name="inactivity_probe"> > + 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. > + </column> > + </group> > + > + <group title="Status"> > + <p> > + Key-value pair of <ref column="is_connected"/> is always updated. > + Other key-value pairs in the status columns may be updated depends > + on the <ref column="target"/> type. > + </p> > + > + <p> > + When <ref column="target"/> specifies a connection method that > + listens for inbound connections (e.g. <code>ptcp:</code> or > + <code>punix:</code>), both <ref column="n_connections"/> and > + <ref column="is_connected"/> may also be updated while the > + remaining key-value pairs are omitted. > + </p> > + > + <p> > + On the other hand, when <ref column="target"/> 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. > + </p> > + > + <column name="is_connected"> > + <code>true</code> if currently connected to this client, > + <code>false</code> otherwise. > + </column> > + > + <column name="status" key="last_error"> > + A human-readable description of the last error on the connection > + to the manager; i.e. <code>strerror(errno)</code>. This key > + will exist only if an error has occurred. > + </column> > + > + <column name="status" key="state" > + type='{"type": "string", "enum": ["set", ["VOID", "BACKOFF", "CONNECTING", "ACTIVE", "IDLE"]]}'> > + <p> > + The state of the connection to the manager: > + </p> > + <dl> > + <dt><code>VOID</code></dt> > + <dd>Connection is disabled.</dd> > + > + <dt><code>BACKOFF</code></dt> > + <dd>Attempting to reconnect at an increasing period.</dd> > + > + <dt><code>CONNECTING</code></dt> > + <dd>Attempting to connect.</dd> > + > + <dt><code>ACTIVE</code></dt> > + <dd>Connected, remote host responsive.</dd> > + > + <dt><code>IDLE</code></dt> > + <dd>Connection is idle. Waiting for response to keep-alive.</dd> > + </dl> > + <p> > + These values may change in the future. They are provided only for > + human consumption. > + </p> > + </column> > + > + <column name="status" key="sec_since_connect" > + type='{"type": "integer", "minInteger": 0}'> > + 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. > + </column> > + > + <column name="status" key="sec_since_disconnect" > + type='{"type": "integer", "minInteger": 0}'> > + The amount of time since this client last disconnected from the > + database (in seconds). Value is empty if client has never > + disconnected. > + </column> > + > + <column name="status" key="locks_held"> > + Space-separated list of the names of OVSDB locks that the connection > + holds. Omitted if the connection does not hold any locks. > + </column> > + > + <column name="status" key="locks_waiting"> > + 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. > + </column> > + > + <column name="status" key="locks_lost"> > + 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. > + </column> > + > + <column name="status" key="n_connections" > + type='{"type": "integer", "minInteger": 2}'> > + When <ref column="target"/> specifies a connection method that > + listens for inbound connections (e.g. <code>ptcp:</code> or > + <code>pssl:</code>) and more than one connection is actually active, > + the value is the number of active connections. Otherwise, this > + key-value pair is omitted. > + </column> > + > + <column name="status" key="bound_port" type='{"type": "integer"}'> > + When <ref column="target"/> is <code>ptcp:</code> or > + <code>pssl:</code>, this is the TCP port on which the OVSDB server is > + listening. (This is particularly useful when <ref > + column="target"/> specifies a port of 0, allowing the kernel to > + choose any available port.) > + </column> > + </group> > + > + <group title="Common Columns"> > + The overall purpose of these columns is described under <code>Common > + Columns</code> at the beginning of this document. > + > + <column name="external_ids"/> > + <column name="other_config"/> > + </group> > + </table> > + <table name="SSL"> > + SSL configuration for ovn-sb database access. > + > + <column name="private_key"> > + Name of a PEM file containing the private key used as the switch's > + identity for SSL connections to the controller. > + </column> > + > + <column name="certificate"> > + 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. > + </column> > + > + <column name="ca_cert"> > + Name of a PEM file containing the CA certificate used to verify > + that the switch is connected to a trustworthy controller. > + </column> > + > + <column name="bootstrap_ca_cert"> > + If set to <code>true</code>, 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. <em>This option exposes the > + SSL connection to a man-in-the-middle attack obtaining the initial > + CA certificate.</em> It may still be useful for bootstrapping. > + </column> > + > + <column name="ssl_protocols"> > + List of SSL protocols to be enabled for SSL connections. The default > + when this option is omitted is <code>TLSv1,TLSv1.1,TLSv1.2</code>. > + </column> > + > + <column name="ssl_ciphers"> > + List of ciphers (in OpenSSL cipher string format) to be supported > + for SSL connections. The default when this option is omitted is > + <code>HIGH:!aNULL:!MD5</code>. > + </column> > + > + <group title="Common Columns"> > + The overall purpose of these columns is described under <code>Common > + Columns</code> at the beginning of this document. > + > + <column name="external_ids"/> > + </group> > + </table> > + > +</database> > diff --git a/tests/automake.mk b/tests/automake.mk > index d978c05..d62304e 100644 > --- a/tests/automake.mk > +++ b/tests/automake.mk > @@ -25,6 +25,7 @@ TESTSUITE_AT = \ > tests/ovn-nbctl.at \ > tests/ovn-sbctl.at \ > tests/ovn-inbctl.at \ > + tests/ovn-isbctl.at \ > tests/ovn-controller.at \ > tests/ovn-controller-vtep.at \ > tests/ovn-macros.at \ > @@ -101,6 +102,7 @@ valgrind_wrappers = \ > tests/valgrind/ovn-northd \ > tests/valgrind/ovn-sbctl \ > tests/valgrind/ovn-inbctl \ > + tests/valgrind/ovn-isbctl \ > tests/valgrind/ovs-appctl \ > tests/valgrind/ovs-ofctl \ > tests/valgrind/ovs-vsctl \ > diff --git a/tests/ovn-isbctl.at b/tests/ovn-isbctl.at > new file mode 100644 > index 0000000..d7a5527 > --- /dev/null > +++ b/tests/ovn-isbctl.at > @@ -0,0 +1,112 @@ > +AT_BANNER([ovn-isbctl]) > + > +# OVN_ISBCTL_TEST_START > +m4_define([OVN_ISBCTL_TEST_START], > + [dnl Create database (ovn-isb). > + AT_KEYWORDS([isbctl]) > + AT_CHECK([ovsdb-tool create ovn-isb.db $abs_top_srcdir/ovn-isb.ovsschema]) > + > + dnl Start ovsdb-servers. > + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=ovnisb_db.pid --unixctl=$OVS_RUNDIR/ovnisb_db.ctl --log-file=ovsdb_inb.log --remote=punix:$OVS_RUNDIR/ovnisb_db.sock ovn-isb.db ], [0], [], [stderr]) > + on_exit "kill `cat ovnisb_db.pid`" > + AT_CHECK([[sed < stderr ' > +/vlog|INFO|opened log file/d > +/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']]) > + AT_CAPTURE_FILE([ovsdb-server.log]) > +]) > + > +# OVN_ISBCTL_TEST_STOP > +m4_define([OVN_ISBCTL_TEST_STOP], > + [AT_CHECK([check_logs "$1"]) > + OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnisb_db.ctl], [$OVS_RUNDIR/ovnisb_db.pid])]) > + > +dnl --------------------------------------------------------------------- > + > +AT_SETUP([ovn-isbctl]) > +OVN_ISBCTL_TEST_START > + > +for az in 1 2; do > + az_uuid=$(ovn-isbctl create availability_zone name=az$az) > + for gw in 1 2; do > + ovn-isbctl --id=@encap create encap type=geneve ip=192.168.0.$az$gw options="csum=true" \ > + -- create gateway availability_zone=$az_uuid name=gw$az$gw encap=@encap hostname=host.gw$az$gw > + for pb in 1 2; do > + ovn-isbctl create port_binding logical_port=lp$az$gw$pb transit_switch="ts$pb" \ > + address="\"aa:aa:aa:aa:0$az:$gw$pb 169.254.$pb.$az$gw/24\"" tunnel_key=$az$gw \ > + availability_zone=$az_uuid gateway=gw$az$gw > + done > + done > +done > + > +AT_CHECK([ovn-isbctl show], [0], [dnl > +availability-zone az1 > + gateway gw11 > + hostname: host.gw11 > + type: geneve > + ip: 192.168.0.11 > + port lp111 > + transit switch: ts1 > + address: [["aa:aa:aa:aa:01:11 169.254.1.11/24"]] > + port lp112 > + transit switch: ts2 > + address: [["aa:aa:aa:aa:01:12 169.254.2.11/24"]] > + gateway gw12 > + hostname: host.gw12 > + type: geneve > + ip: 192.168.0.12 > + port lp121 > + transit switch: ts1 > + address: [["aa:aa:aa:aa:01:21 169.254.1.12/24"]] > + port lp122 > + transit switch: ts2 > + address: [["aa:aa:aa:aa:01:22 169.254.2.12/24"]] > +availability-zone az2 > + gateway gw21 > + hostname: host.gw21 > + type: geneve > + ip: 192.168.0.21 > + port lp211 > + transit switch: ts1 > + address: [["aa:aa:aa:aa:02:11 169.254.1.21/24"]] > + port lp212 > + transit switch: ts2 > + address: [["aa:aa:aa:aa:02:12 169.254.2.21/24"]] > + gateway gw22 > + hostname: host.gw22 > + type: geneve > + ip: 192.168.0.22 > + port lp221 > + transit switch: ts1 > + address: [["aa:aa:aa:aa:02:21 169.254.1.22/24"]] > + port lp222 > + transit switch: ts2 > + address: [["aa:aa:aa:aa:02:22 169.254.2.22/24"]] > +]) > + > +AT_CHECK([ovn-isbctl show az2], [0], [dnl > +availability-zone az2 > + gateway gw21 > + hostname: host.gw21 > + type: geneve > + ip: 192.168.0.21 > + port lp211 > + transit switch: ts1 > + address: [["aa:aa:aa:aa:02:11 169.254.1.21/24"]] > + port lp212 > + transit switch: ts2 > + address: [["aa:aa:aa:aa:02:12 169.254.2.21/24"]] > + gateway gw22 > + hostname: host.gw22 > + type: geneve > + ip: 192.168.0.22 > + port lp221 > + transit switch: ts1 > + address: [["aa:aa:aa:aa:02:21 169.254.1.22/24"]] > + port lp222 > + transit switch: ts2 > + address: [["aa:aa:aa:aa:02:22 169.254.2.22/24"]] > +]) > + > + > +OVN_ISBCTL_TEST_STOP > +AT_CLEANUP > diff --git a/tests/testsuite.at b/tests/testsuite.at > index 20dbccb..ea6b04a 100644 > --- a/tests/testsuite.at > +++ b/tests/testsuite.at > @@ -26,6 +26,7 @@ m4_include([tests/ovn-northd.at]) > m4_include([tests/ovn-nbctl.at]) > m4_include([tests/ovn-sbctl.at]) > m4_include([tests/ovn-inbctl.at]) > +m4_include([tests/ovn-isbctl.at]) > m4_include([tests/ovn-controller.at]) > m4_include([tests/ovn-controller-vtep.at]) > m4_include([tests/checkpatch.at]) > 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 30c52fa..2bea391 100644 > --- a/utilities/automake.mk > +++ b/utilities/automake.mk > @@ -8,6 +8,7 @@ man_MANS += \ > utilities/ovn-nbctl.8 \ > utilities/ovn-sbctl.8 \ > utilities/ovn-inbctl.8 \ > + utilities/ovn-isbctl.8 \ > utilities/ovn-trace.8 \ > utilities/ovn-detrace.1 \ > utilities/ovn-appctl.8 > @@ -30,6 +31,7 @@ EXTRA_DIST += \ > utilities/ovn-docker-underlay-driver.in \ > utilities/ovn-nbctl.8.xml \ > utilities/ovn-inbctl.8.xml \ > + utilities/ovn-isbctl.8.xml \ > utilities/ovn-appctl.8.xml \ > utilities/ovn-trace.8.xml \ > utilities/ovn-detrace.in \ > @@ -51,6 +53,7 @@ CLEANFILES += \ > utilities/ovn-nbctl.8 \ > utilities/ovn-sbctl.8 \ > utilities/ovn-inbctl.8 \ > + utilities/ovn-isbctl.8 \ > utilities/ovn-trace.8 \ > utilities/ovn-detrace.1 \ > utilities/ovn-detrace \ > @@ -74,6 +77,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.8.xml b/utilities/ovn-isbctl.8.xml > new file mode 100644 > index 0000000..65b7557 > --- /dev/null > +++ b/utilities/ovn-isbctl.8.xml > @@ -0,0 +1,148 @@ > +<?xml version="1.0" encoding="utf-8"?> > +<manpage program="ovn-isbctl" section="8" title="ovn-isbctl"> > + <h1>Name</h1> > + <p>ovn-isbctl -- Open Virtual Network interconnection southbound db management utility</p> > + > + <h1>Synopsis</h1> > + <p><code>ovn-isbctl</code> [<var>options</var>] <var>command</var> [<var>arg</var>...]</p> > + > + <h1>Description</h1> > + <p>This utility can be used to manage the OVN interconnection southbound database.</p> > + > + <h1>General Commands</h1> > + > + <dl> > + <dt><code>init</code></dt> > + <dd> > + Initializes the database, if it is empty. If the database has already > + been initialized, this command has no effect. > + </dd> > + > + <dt><code>show [<var>availability_zone</var>]</code></dt> > + <dd> > + Prints a brief overview of the database contents. If > + <var>availability_zone</var> is provided, only records related to that > + availability zone are shown. > + </dd> > + </dl> > + > + <h1>Database Commands</h1> > + <p>These commands query and modify the contents of <code>ovsdb</code> tables. > + They are a slight abstraction of the <code>ovsdb</code> interface and > + as such they operate at a lower level than other <code>ovn-isbctl</code> commands.</p> > + <p><var>Identifying Tables, Records, and Columns</var></p> > + <p>Each of these commands has a <var>table</var> parameter to identify a table > + within the database. Many of them also take a <var>record</var> parameter > + that identifies a particular record within a table. The <var>record</var> > + parameter may be the UUID for a record, which may be abbreviated to its > + first 4 (or more) hex digits, as long as that is unique. Many tables offer > + additional ways to identify records. Some commands also take > + <var>column</var> parameters that identify a particular field within the > + records in a table.</p> > + > + <p> > + For a list of tables and their columns, see <code>ovn-isb</code>(5) or > + see the table listing from the <code>--help</code> option. > + </p> > + > + <p> > + Record names must be specified in full and with correct capitalization, > + except that UUIDs may be abbreviated to their first 4 (or more) hex > + digits, as long as that is unique within the table. Names of tables and > + columns are not case-sensitive, and <code>-</code> and <code>_</code> are > + treated interchangeably. Unique abbreviations of table and column names > + are acceptable, e.g. <code>g</code> or <code>gatew</code> is sufficient > + to identify the <code>Gateway</code> table. > + </p> > + > + <xi:include href="lib/db-ctl-base.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> > + > + <h1>Remote Connectivity Commands</h1> > + <dl> > + <dt><code>get-connection</code></dt> > + <dd> > + Prints the configured connection(s). > + </dd> > + > + <dt><code>del-connection</code></dt> > + <dd> > + Deletes the configured connection(s). > + </dd> > + > + <dt>[<code>--inactivity-probe=</code><var>msecs</var>] <code>set-connection</code> <var>target</var>...</dt> > + <dd> > + Sets the configured manager target or targets. Use > + <code>--inactivity-probe=</code><var>msecs</var> to override the default > + idle connection inactivity probe time. Use 0 to disable inactivity probes. > + </dd> > + </dl> > + > + <h1>SSL Configuration Commands</h1> > + <dl> > + <dt><code>get-ssl</code></dt> > + <dd> > + Prints the SSL configuration. > + </dd> > + > + <dt><code>del-ssl</code></dt> > + <dd> > + Deletes the current SSL configuration. > + </dd> > + > + <dt>[<code>--bootstrap</code>] <code>set-ssl</code> > + <var>private-key</var> <var>certificate</var> <var>ca-cert</var> > + [<var>ssl-protocol-list</var> [<var>ssl-cipher-list</var>]]</dt> > + <dd> > + Sets the SSL configuration. > + </dd> > + </dl> > + > + <h1>Options</h1> > + > + <dl> > + <dt><code>--db</code> <var>database</var></dt> > + <dd> > + The OVSDB database remote to contact. If the <env>OVN_ISB_DB</env> > + environment variable is set, its value is used as the default. > + Otherwise, the default is <code>unix:@RUNDIR@/ovnisb_db.sock</code>, but this > + default is unlikely to be useful outside of single-machine OVN test > + environments. > + </dd> > + > + <dt><code>--leader-only</code></dt> > + <dt><code>--no-leader-only</code></dt> > + <dd> > + By default, or with <code>--leader-only</code>, when the database server > + is a clustered database, <code>ovn-isbctl</code> will avoid servers other > + than the cluster leader. This ensures that any data that > + <code>ovn-isbctl</code> reads and reports is up-to-date. With > + <code>--no-leader-only</code>, <code>ovn-isbctl</code> will use any server > + in the cluster, which means that for read-only transactions it can report > + and act on stale data (transactions that modify the database are always > + serialized even with <code>--no-leader-only</code>). Refer to > + <code>Understanding Cluster Consistency</code> in <code>ovsdb</code>(7) > + for more information. > + </dd> > + </dl> > + > + <h1>Logging options</h1> > + <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> > + > + <h1>Table Formatting Options</h1> > + These options control the format of output from the <code>list</code> and > + <code>find</code> commands. > + <xi:include href="lib/table.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> > + > + <h2>PKI Options</h2> > + <p> > + PKI configuration is required to use SSL for the connection to the > + database. > + </p> > + <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> > + <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> > + > + <h2>Other Options</h2> > + > + <xi:include href="lib/common.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> > + > +</manpage> > diff --git a/utilities/ovn-isbctl.c b/utilities/ovn-isbctl.c > new file mode 100644 > index 0000000..9ac3a4b > --- /dev/null > +++ b/utilities/ovn-isbctl.c > @@ -0,0 +1,1015 @@ > +/* > + * 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 <config.h> > + > +#include <ctype.h> > +#include <getopt.h> > +#include <inttypes.h> > +#include <stdarg.h> > +#include <stdlib.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/shash.h" > +#include "openvswitch/vconn.h" > +#include "openvswitch/vlog.h" > +#include "lib/ovn-isb-idl.h" > +#include "lib/ovn-util.h" > +#include "openvswitch/poll-loop.h" > +#include "process.h" > +#include "sset.h" > +#include "stream-ssl.h" > +#include "stream.h" > +#include "table.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, true, false); > + 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: > + ovs_abort(0, "Internal error when parsing option %d.", c); > + > + 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. > + * Now empty, just keep the framework for future additions. */ > +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; > +}; > + > +static void > +isbctl_init(struct ctl_context *ctx OVS_UNUSED) > +{ > +} > + > +static char * > +az_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist, > + const struct isbrec_availability_zone **az_p) > +{ > + const struct isbrec_availability_zone *az = NULL; > + *az_p = NULL; > + > + struct uuid az_uuid; > + bool is_uuid = uuid_from_string(&az_uuid, id); > + if (is_uuid) { > + az = isbrec_availability_zone_get_for_uuid(ctx->idl, &az_uuid); > + } > + > + if (!az) { > + const struct isbrec_availability_zone *iter; > + > + ISBREC_AVAILABILITY_ZONE_FOR_EACH (iter, ctx->idl) { > + if (!strcmp(iter->name, id)) { > + az = iter; > + break; > + } > + } > + } > + > + if (!az && must_exist) { > + return xasprintf("%s: availability zone %s not found", > + id, is_uuid ? "UUID" : "name"); > + } > + > + *az_p = az; > + return NULL; > +} > + > +/* Data structures for the show command, which constructs the hierarchy > + * of availability_zone:gateway:port_binding. */ > +struct gw_data { > + const struct isbrec_gateway *isb_gw; > + /* With node type struct isbrec_port_binding. > + * We don't need to search port-binding, but using shash makes > + * sorting convenient. */ > + struct shash pbs; > +}; > + > +struct az_data { > + const struct isbrec_availability_zone *isb_az; > + /* With node type struct gw_data */ > + struct shash gws; > +}; > + > +static void > +init_az_info(struct ctl_context *ctx, struct shash *azs) > +{ > + const struct isbrec_availability_zone *az; > + ISBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->idl) { > + struct az_data *az_data = xmalloc(sizeof *az_data); > + shash_init(&az_data->gws); > + az_data->isb_az = az; > + shash_add(azs, az->name, az_data); > + } > + > + const struct isbrec_gateway *gw; > + ISBREC_GATEWAY_FOR_EACH (gw, ctx->idl) { > + struct az_data *az_data = shash_find_data(azs, > + gw->availability_zone->name); > + ovs_assert(az_data); > + struct gw_data *gw_data = xmalloc(sizeof *gw_data); > + gw_data->isb_gw = gw; > + shash_init(&gw_data->pbs); > + shash_add(&az_data->gws, gw->name, gw_data); > + } > + > + const struct isbrec_port_binding *pb; > + ISBREC_PORT_BINDING_FOR_EACH (pb, ctx->idl) { > + struct az_data *az_data = shash_find_data(azs, > + pb->availability_zone->name); > + ovs_assert(az_data); > + struct gw_data *gw_data = shash_find_data(&az_data->gws, pb->gateway); > + if (!gw_data) { > + continue; > + } > + shash_add(&gw_data->pbs, pb->logical_port, pb); > + } > +} > + > +static void > +destroy_az_info(struct shash *azs) > +{ > + struct shash_node *az_node; > + SHASH_FOR_EACH (az_node, azs) { > + struct az_data *az_data = az_node->data; > + struct shash_node *gw_node; > + SHASH_FOR_EACH (gw_node, &az_data->gws) { > + struct gw_data *gw_data = gw_node->data; > + shash_destroy(&gw_data->pbs); > + free(gw_data); > + } > + shash_destroy(&az_data->gws); > + free(az_data); > + } > + shash_destroy(azs); > +} > + > +static void > +print_az(struct az_data *az, struct ds *s) > +{ > + ds_put_format(s, "availability-zone %s\n", az->isb_az->name); > + > + const struct shash_node **nodes = shash_sort(&az->gws); > + for (int g = 0; g < shash_count(&az->gws); g++) { > + struct gw_data *gw = nodes[g]->data; > + > + ds_put_format(s, " gateway %s\n", gw->isb_gw->name); > + if (gw->isb_gw->hostname[0]) { > + ds_put_format(s, " hostname: %s\n", gw->isb_gw->hostname); > + } > + > + for (int i = 0; i < gw->isb_gw->n_encaps; i++) { > + ds_put_format(s, " type: %s\n", > + gw->isb_gw->encaps[i]->type); > + ds_put_format(s, " ip: %s\n", > + gw->isb_gw->encaps[i]->ip); > + } > + > + const struct shash_node **pb_nodes = shash_sort(&gw->pbs); > + for (int p = 0; p < shash_count(&gw->pbs); p++) { > + const struct isbrec_port_binding *pb = pb_nodes[p]->data; > + ds_put_format(s, " port %s\n", > + pb->logical_port); > + ds_put_format(s, " transit switch: %s\n", > + pb->transit_switch); > + ds_put_format(s, " address: [\"%s\"]\n", > + pb->address); > + } > + free(pb_nodes); > + } > + free(nodes); > +} > + > +static void > +isbctl_show(struct ctl_context *ctx) > +{ > + /* Availability zones with the gateways and port-bindings data. Node > + * type is struct az_data. */ > + struct shash azs = SHASH_INITIALIZER(&azs); > + init_az_info(ctx, &azs); > + > + const struct isbrec_availability_zone *az; > + > + if (ctx->argc == 2) { > + char *error = az_by_name_or_uuid(ctx, ctx->argv[1], false, &az); > + if (error) { > + ctx->error = error; > + goto error; > + } > + if (az) { > + struct az_data *az_data = shash_find_data(&azs, az->name); > + ovs_assert(az_data); > + print_az(az_data, &ctx->output); > + } > + } else { > + const struct shash_node **nodes = shash_sort(&azs); > + for (int i = 0; i < shash_count(&azs); i++) { > + struct az_data *az_data = nodes[i]->data; > + print_az(az_data, &ctx->output); > + } > + free(nodes); > + } > + > +error: > + destroy_az_info(&azs); > +} > + > +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}, > + > + [ISBREC_TABLE_GATEWAY].row_ids[0] = > + {&isbrec_gateway_col_name, NULL, NULL}, > + > + [ISBREC_TABLE_PORT_BINDING].row_ids[0] = > + {&isbrec_port_binding_col_logical_port, NULL, NULL}, > + > + [ISBREC_TABLE_DATAPATH_BINDING].row_ids[0] = > + {&isbrec_datapath_binding_col_transit_switch, 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, NULL); > + 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 }, > + { "show", 0, 1, "[AZ]", NULL, isbctl_show, NULL, "", RO }, > + > + /* 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, > + NULL, isbctl_exit); > + ctl_register_commands(isbctl_commands); > +} > -- > 2.1.0 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
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..3d3b54e --- /dev/null +++ b/ovn-isb.ovsschema @@ -0,0 +1,129 @@ +{ + "name": "OVN_IC_Southbound", + "version": "1.0.0", + "cksum": "3575687822 6054", + "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"}}}, + "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..0024165 --- /dev/null +++ b/ovn-isb.xml @@ -0,0 +1,582 @@ +<?xml version="1.0" encoding="utf-8"?> +<database name="ovn-isb" title="OVN Interconnection Southbound Database"> + <p> + This database holds configuration and state for interconnecting different + OVN deployments. The content of the database is populated and used by the + <code>ovn-ic</code> program in each OVN deployment, and not supposed to be + directly used by CMS or end user. + </p> + + <p> + The OVN Interconnection Southbound database is shared by + <code>ovn-ic</code> 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 <code>ovn-ic</code> program in each deployment is + responsible for syncing the data between this database and the its own + northbound and southbound databases. + </p> + + <h2>Database Structure</h2> + + <p> + The OVN Interconnection Southbound database contains classes of data with + different properties, as described in the sections below. + </p> + + <h3>Availability Zone Specific Information</h3> + + <p> + 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. + </p> + + <p> + The <ref table="Availability_Zone"/>, <ref table="Gateway"/>, <ref + table="Encap"/> and <ref table="Port_Binding"/> tables are the availability + zone specific tables. + </p> + + <h3>Global Information</h3> + + <p> + The data that does not belong to any specific availability zone but is + common for all availability zones. + </p> + + <p> + The <ref table="Datapath_Binding"/> table contains the common datapath + binding information. + </p> + + <h3>Common Columns</h3> + <p> + Each of the tables in this database contains a special column, named + <code>external_ids</code>. This column has the same form and purpose each + place it appears. + </p> + + <dl> + <dt><code>external_ids</code>: map of string-string pairs</dt> + <dd> + Key-value pairs for use by <code>ovn-ic</code>. + </dd> + </dl> + <table name="ISB_Global" title="IC Southbound configuration"> + <p> + Interconnection Southbound configuration. This table must have exactly + one row. + </p> + + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + + <column name="options"> + </column> + </group> + + <group title="Connection Options"> + <column name="connections"> + 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 <ref table="Connection"/> + table for more information. + </column> + <column name="ssl"> + Global SSL configuration. + </column> + </group> + </table> + + <table name="Availability_Zone" title="Availability Zone Information"> + <p> + 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 + <code>ovn-northd</code> daemon. + </p> + + <column name="name"> + A name that uniquely identifies the availability zone. + </column> + </table> + + <table name="Gateway" title="Interconnection Gateway Information"> + <p> + Each row in this table represents a interconnection gateway chassis in an + availability zone. + </p> + + <column name="name"> + The name of the gateway. See <ref table="Chassis" column="name"/> column + of the OVN Southbound database's <ref table="Chassis" + db="OVN_Southbound"/> table. + </column> + + <column name="availability_zone"> + The availabilty zone that the gateway belongs to. + </column> + + <column name="hostname"> + The hostname of the gateway. + </column> + + <group title="Common Columns"> + The overall purpose of these columns is described under <code>Common + Columns</code> at the beginning of this document. + + <column name="external_ids"/> + </group> + + <group title="Encapsulation Configuration"> + <p> + OVN uses encapsulation to transmit logical dataplane packets + between gateways. + </p> + + <column name="encaps"> + Points to supported encapsulation configurations to transmit + logical dataplane packets to this gateway. Each entry is a <ref + table="Encap"/> record that describes the configuration. + See <ref table="Chassis" column="encaps"/> column + of the OVN Southbound database's <ref table="Chassis" + db="OVN_Southbound"/> table. + </column> + </group> + + </table> + + <table name="Encap" title="Encapsulation Types"> + <p> + The <ref column="encaps" table="Gateway"/> column in the <ref + table="Gateway"/> table refers to rows in this table to identify + how OVN may transmit logical dataplane packets to this gateway. + </p> + + <column name="type"> + The encapsulation to use to transmit packets to this gateway. + See <ref table="Encap" column="type"/> column + of the OVN Southbound database's <ref table="Encap" + db="OVN_Southbound"/> table. + </column> + + <column name="options"> + Options for configuring the encapsulation, which may be <ref + column="type"/> specific. See <ref table="Encap" column="options"/> + column of the OVN Southbound database's <ref table="Encap" + db="OVN_Southbound"/> table. + </column> + + <column name="ip"> + The IPv4 address of the encapsulation tunnel endpoint. + </column> + + <column name="gateway_name"> + The name of the gateway that created this encap. + </column> + </table> + + <table name="Datapath_Binding" title="Transit Switch Datapath Bindings"> + <p> + Each row in this table represents a logical datapath for a transit + logical switch configured in the OVN Interconnection Northbound + database's <ref table="Transit_Switch" db="OVN_IC_Northbound"/> table. + </p> + + <column name="transit_switch"> + The name of the transit logical switch that is configured in the OVN + Interconnection Northbound database's <ref table="Transit_Switch" + db="OVN_IC_Northbound"/> table. + </column> + + <column name="tunnel_key"> + <p> + The tunnel key value to which the logical datapath is bound. The key + can be generated by any <code>ovn-ic</code> 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. + </p> + + <p> + For more information about the meanings of a tunnel key, + see <ref table="Datapath_Binding" column="tunnel_key"/> column of the OVN + Southbound database's <ref table="Datapath_Binding" db="OVN_Southbound"/> + table. + </p> + </column> + + <group title="Common Columns"> + The overall purpose of these columns is described under <code>Common + Columns</code> at the beginning of this document. + + <column name="external_ids"/> + </group> + </table> + + <table name="Port_Binding" title="Transit Port Bindings"> + <p> + 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. + </p> + + <group title="Core Features"> + <column name="transit_switch"> + The name of the transit switch that the corresponding logical port + belongs to. + </column> + + <column name="logical_port"> + A logical port, taken from <ref table="Logical_Switch_Port" + column="name" db="OVN_Northbound"/> in the OVN_Northbound + database's <ref table="Logical_Switch_Port" db="OVN_Northbound"/> + table. The logical port name must be unique across all availability + zones. + </column> + + <column name="availability_zone"> + The availability zone that the port belongs to. + </column> + + <column name="encap"> + Points to supported encapsulation configurations to transmit + logical dataplane packets to this gateway. Each entry is a <ref + table="Encap"/> record that describes the configuration. + </column> + + <column name="gateway"> + The name of the gateway that this port is physically located. + </column> + + <column name="tunnel_key"> + <p> + 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 <code>ovn-ic</code> but the same key is + shared by all availability zones so that the packets can go through + the datapath pipelines of different availability zones. + </p> + <p> + The tunnel ID must be unique within the scope of a logical datapath. + </p> + <p> + For more information about tunnel key, see <ref table="Port_Binding" + column="tunnel_key"/> column of the OVN Southbound database's <ref + table="Port_Binding" db="OVN_Southbound"/> table. + </p> + </column> + + <column name="address"> + <p> + 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 <ref table="Logical_Router_Port" column="mac"/> + column followed by the values in <ref table="Logical_Router_Port" + column="networks"/> column in <ref table="Logical_Router_Port" + db="OVN_Northbound"/> table. + </p> + </column> + + </group> + + <group title="Common Columns"> + <column name="external_ids"> + <p> + See <em>External IDs</em> at the beginning of this document. + </p> + </column> + </group> + </table> + + <table name="Connection" title="OVSDB client connections."> + <p> + Configuration for a database connection to an Open vSwitch database + (OVSDB) client. + </p> + + <p> + This table primarily configures the Open vSwitch database server + (<code>ovsdb-server</code>). + </p> + + <p> + The Open vSwitch database server can initiate and maintain active + connections to remote clients. It can also listen for database + connections. + </p> + + <group title="Core Features"> + <column name="target"> + <p>Connection methods for clients.</p> + <p> + The following connection methods are currently supported: + </p> + <dl> + <dt><code>ssl:<var>host</var></code>[<code>:<var>port</var></code>]</dt> + <dd> + <p> + The specified SSL <var>port</var> on the given <var>host</var>, + 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 <ref table="SSL"/> table. + </p> + <p> + If <var>port</var> is not specified, it defaults to 6640. + </p> + <p> + SSL support is an optional feature that is not always + built as part of Open vSwitch. + </p> + </dd> + + <dt><code>tcp:<var>host</var></code>[<code>:<var>port</var></code>]</dt> + <dd> + <p> + The specified TCP <var>port</var> on the given <var>host</var>, + which can either be a DNS name (if built with unbound library) or + an IP address (IPv4 or IPv6). If <var>host</var> is an IPv6 + address, wrap it in square brackets, e.g. <code>tcp:[::1]:6640</code>. + </p> + <p> + If <var>port</var> is not specified, it defaults to 6640. + </p> + </dd> + <dt><code>pssl:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> + <dd> + <p> + Listens for SSL connections on the specified TCP <var>port</var>. + Specify 0 for <var>port</var> to have the kernel automatically + choose an available port. If <var>host</var>, 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 + <var>host</var> is an IPv6 address, wrap in square brackets, + e.g. <code>pssl:6640:[::1]</code>. If <var>host</var> 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 + <ref table="SSL"/> table. + </p> + <p> + If <var>port</var> is not specified, it defaults to 6640. + </p> + <p> + SSL support is an optional feature that is not always built as + part of Open vSwitch. + </p> + </dd> + <dt><code>ptcp:</code>[<var>port</var>][<code>:<var>host</var></code>]</dt> + <dd> + <p> + Listens for connections on the specified TCP <var>port</var>. + Specify 0 for <var>port</var> to have the kernel automatically + choose an available port. If <var>host</var>, 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 + <var>host</var> is an IPv6 address, wrap it in square brackets, + e.g. <code>ptcp:6640:[::1]</code>. If <var>host</var> is not + specified then it listens only on IPv4 addresses. + </p> + <p> + If <var>port</var> is not specified, it defaults to 6640. + </p> + </dd> + </dl> + <p>When multiple clients are configured, the <ref column="target"/> + values must be unique. Duplicate <ref column="target"/> values yield + unspecified results.</p> + </column> + + </group> + + <group title="Client Failure Detection and Handling"> + <column name="max_backoff"> + Maximum number of milliseconds to wait between connection attempts. + Default is implementation-specific. + </column> + + <column name="inactivity_probe"> + 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. + </column> + </group> + + <group title="Status"> + <p> + Key-value pair of <ref column="is_connected"/> is always updated. + Other key-value pairs in the status columns may be updated depends + on the <ref column="target"/> type. + </p> + + <p> + When <ref column="target"/> specifies a connection method that + listens for inbound connections (e.g. <code>ptcp:</code> or + <code>punix:</code>), both <ref column="n_connections"/> and + <ref column="is_connected"/> may also be updated while the + remaining key-value pairs are omitted. + </p> + + <p> + On the other hand, when <ref column="target"/> 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. + </p> + + <column name="is_connected"> + <code>true</code> if currently connected to this client, + <code>false</code> otherwise. + </column> + + <column name="status" key="last_error"> + A human-readable description of the last error on the connection + to the manager; i.e. <code>strerror(errno)</code>. This key + will exist only if an error has occurred. + </column> + + <column name="status" key="state" + type='{"type": "string", "enum": ["set", ["VOID", "BACKOFF", "CONNECTING", "ACTIVE", "IDLE"]]}'> + <p> + The state of the connection to the manager: + </p> + <dl> + <dt><code>VOID</code></dt> + <dd>Connection is disabled.</dd> + + <dt><code>BACKOFF</code></dt> + <dd>Attempting to reconnect at an increasing period.</dd> + + <dt><code>CONNECTING</code></dt> + <dd>Attempting to connect.</dd> + + <dt><code>ACTIVE</code></dt> + <dd>Connected, remote host responsive.</dd> + + <dt><code>IDLE</code></dt> + <dd>Connection is idle. Waiting for response to keep-alive.</dd> + </dl> + <p> + These values may change in the future. They are provided only for + human consumption. + </p> + </column> + + <column name="status" key="sec_since_connect" + type='{"type": "integer", "minInteger": 0}'> + 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. + </column> + + <column name="status" key="sec_since_disconnect" + type='{"type": "integer", "minInteger": 0}'> + The amount of time since this client last disconnected from the + database (in seconds). Value is empty if client has never + disconnected. + </column> + + <column name="status" key="locks_held"> + Space-separated list of the names of OVSDB locks that the connection + holds. Omitted if the connection does not hold any locks. + </column> + + <column name="status" key="locks_waiting"> + 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. + </column> + + <column name="status" key="locks_lost"> + 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. + </column> + + <column name="status" key="n_connections" + type='{"type": "integer", "minInteger": 2}'> + When <ref column="target"/> specifies a connection method that + listens for inbound connections (e.g. <code>ptcp:</code> or + <code>pssl:</code>) and more than one connection is actually active, + the value is the number of active connections. Otherwise, this + key-value pair is omitted. + </column> + + <column name="status" key="bound_port" type='{"type": "integer"}'> + When <ref column="target"/> is <code>ptcp:</code> or + <code>pssl:</code>, this is the TCP port on which the OVSDB server is + listening. (This is particularly useful when <ref + column="target"/> specifies a port of 0, allowing the kernel to + choose any available port.) + </column> + </group> + + <group title="Common Columns"> + The overall purpose of these columns is described under <code>Common + Columns</code> at the beginning of this document. + + <column name="external_ids"/> + <column name="other_config"/> + </group> + </table> + <table name="SSL"> + SSL configuration for ovn-sb database access. + + <column name="private_key"> + Name of a PEM file containing the private key used as the switch's + identity for SSL connections to the controller. + </column> + + <column name="certificate"> + 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. + </column> + + <column name="ca_cert"> + Name of a PEM file containing the CA certificate used to verify + that the switch is connected to a trustworthy controller. + </column> + + <column name="bootstrap_ca_cert"> + If set to <code>true</code>, 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. <em>This option exposes the + SSL connection to a man-in-the-middle attack obtaining the initial + CA certificate.</em> It may still be useful for bootstrapping. + </column> + + <column name="ssl_protocols"> + List of SSL protocols to be enabled for SSL connections. The default + when this option is omitted is <code>TLSv1,TLSv1.1,TLSv1.2</code>. + </column> + + <column name="ssl_ciphers"> + List of ciphers (in OpenSSL cipher string format) to be supported + for SSL connections. The default when this option is omitted is + <code>HIGH:!aNULL:!MD5</code>. + </column> + + <group title="Common Columns"> + The overall purpose of these columns is described under <code>Common + Columns</code> at the beginning of this document. + + <column name="external_ids"/> + </group> + </table> + +</database> diff --git a/tests/automake.mk b/tests/automake.mk index d978c05..d62304e 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -25,6 +25,7 @@ TESTSUITE_AT = \ tests/ovn-nbctl.at \ tests/ovn-sbctl.at \ tests/ovn-inbctl.at \ + tests/ovn-isbctl.at \ tests/ovn-controller.at \ tests/ovn-controller-vtep.at \ tests/ovn-macros.at \ @@ -101,6 +102,7 @@ valgrind_wrappers = \ tests/valgrind/ovn-northd \ tests/valgrind/ovn-sbctl \ tests/valgrind/ovn-inbctl \ + tests/valgrind/ovn-isbctl \ tests/valgrind/ovs-appctl \ tests/valgrind/ovs-ofctl \ tests/valgrind/ovs-vsctl \ diff --git a/tests/ovn-isbctl.at b/tests/ovn-isbctl.at new file mode 100644 index 0000000..d7a5527 --- /dev/null +++ b/tests/ovn-isbctl.at @@ -0,0 +1,112 @@ +AT_BANNER([ovn-isbctl]) + +# OVN_ISBCTL_TEST_START +m4_define([OVN_ISBCTL_TEST_START], + [dnl Create database (ovn-isb). + AT_KEYWORDS([isbctl]) + AT_CHECK([ovsdb-tool create ovn-isb.db $abs_top_srcdir/ovn-isb.ovsschema]) + + dnl Start ovsdb-servers. + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=ovnisb_db.pid --unixctl=$OVS_RUNDIR/ovnisb_db.ctl --log-file=ovsdb_inb.log --remote=punix:$OVS_RUNDIR/ovnisb_db.sock ovn-isb.db ], [0], [], [stderr]) + on_exit "kill `cat ovnisb_db.pid`" + AT_CHECK([[sed < stderr ' +/vlog|INFO|opened log file/d +/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']]) + AT_CAPTURE_FILE([ovsdb-server.log]) +]) + +# OVN_ISBCTL_TEST_STOP +m4_define([OVN_ISBCTL_TEST_STOP], + [AT_CHECK([check_logs "$1"]) + OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnisb_db.ctl], [$OVS_RUNDIR/ovnisb_db.pid])]) + +dnl --------------------------------------------------------------------- + +AT_SETUP([ovn-isbctl]) +OVN_ISBCTL_TEST_START + +for az in 1 2; do + az_uuid=$(ovn-isbctl create availability_zone name=az$az) + for gw in 1 2; do + ovn-isbctl --id=@encap create encap type=geneve ip=192.168.0.$az$gw options="csum=true" \ + -- create gateway availability_zone=$az_uuid name=gw$az$gw encap=@encap hostname=host.gw$az$gw + for pb in 1 2; do + ovn-isbctl create port_binding logical_port=lp$az$gw$pb transit_switch="ts$pb" \ + address="\"aa:aa:aa:aa:0$az:$gw$pb 169.254.$pb.$az$gw/24\"" tunnel_key=$az$gw \ + availability_zone=$az_uuid gateway=gw$az$gw + done + done +done + +AT_CHECK([ovn-isbctl show], [0], [dnl +availability-zone az1 + gateway gw11 + hostname: host.gw11 + type: geneve + ip: 192.168.0.11 + port lp111 + transit switch: ts1 + address: [["aa:aa:aa:aa:01:11 169.254.1.11/24"]] + port lp112 + transit switch: ts2 + address: [["aa:aa:aa:aa:01:12 169.254.2.11/24"]] + gateway gw12 + hostname: host.gw12 + type: geneve + ip: 192.168.0.12 + port lp121 + transit switch: ts1 + address: [["aa:aa:aa:aa:01:21 169.254.1.12/24"]] + port lp122 + transit switch: ts2 + address: [["aa:aa:aa:aa:01:22 169.254.2.12/24"]] +availability-zone az2 + gateway gw21 + hostname: host.gw21 + type: geneve + ip: 192.168.0.21 + port lp211 + transit switch: ts1 + address: [["aa:aa:aa:aa:02:11 169.254.1.21/24"]] + port lp212 + transit switch: ts2 + address: [["aa:aa:aa:aa:02:12 169.254.2.21/24"]] + gateway gw22 + hostname: host.gw22 + type: geneve + ip: 192.168.0.22 + port lp221 + transit switch: ts1 + address: [["aa:aa:aa:aa:02:21 169.254.1.22/24"]] + port lp222 + transit switch: ts2 + address: [["aa:aa:aa:aa:02:22 169.254.2.22/24"]] +]) + +AT_CHECK([ovn-isbctl show az2], [0], [dnl +availability-zone az2 + gateway gw21 + hostname: host.gw21 + type: geneve + ip: 192.168.0.21 + port lp211 + transit switch: ts1 + address: [["aa:aa:aa:aa:02:11 169.254.1.21/24"]] + port lp212 + transit switch: ts2 + address: [["aa:aa:aa:aa:02:12 169.254.2.21/24"]] + gateway gw22 + hostname: host.gw22 + type: geneve + ip: 192.168.0.22 + port lp221 + transit switch: ts1 + address: [["aa:aa:aa:aa:02:21 169.254.1.22/24"]] + port lp222 + transit switch: ts2 + address: [["aa:aa:aa:aa:02:22 169.254.2.22/24"]] +]) + + +OVN_ISBCTL_TEST_STOP +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 20dbccb..ea6b04a 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -26,6 +26,7 @@ m4_include([tests/ovn-northd.at]) m4_include([tests/ovn-nbctl.at]) m4_include([tests/ovn-sbctl.at]) m4_include([tests/ovn-inbctl.at]) +m4_include([tests/ovn-isbctl.at]) m4_include([tests/ovn-controller.at]) m4_include([tests/ovn-controller-vtep.at]) m4_include([tests/checkpatch.at]) 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 30c52fa..2bea391 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -8,6 +8,7 @@ man_MANS += \ utilities/ovn-nbctl.8 \ utilities/ovn-sbctl.8 \ utilities/ovn-inbctl.8 \ + utilities/ovn-isbctl.8 \ utilities/ovn-trace.8 \ utilities/ovn-detrace.1 \ utilities/ovn-appctl.8 @@ -30,6 +31,7 @@ EXTRA_DIST += \ utilities/ovn-docker-underlay-driver.in \ utilities/ovn-nbctl.8.xml \ utilities/ovn-inbctl.8.xml \ + utilities/ovn-isbctl.8.xml \ utilities/ovn-appctl.8.xml \ utilities/ovn-trace.8.xml \ utilities/ovn-detrace.in \ @@ -51,6 +53,7 @@ CLEANFILES += \ utilities/ovn-nbctl.8 \ utilities/ovn-sbctl.8 \ utilities/ovn-inbctl.8 \ + utilities/ovn-isbctl.8 \ utilities/ovn-trace.8 \ utilities/ovn-detrace.1 \ utilities/ovn-detrace \ @@ -74,6 +77,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.8.xml b/utilities/ovn-isbctl.8.xml new file mode 100644 index 0000000..65b7557 --- /dev/null +++ b/utilities/ovn-isbctl.8.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="utf-8"?> +<manpage program="ovn-isbctl" section="8" title="ovn-isbctl"> + <h1>Name</h1> + <p>ovn-isbctl -- Open Virtual Network interconnection southbound db management utility</p> + + <h1>Synopsis</h1> + <p><code>ovn-isbctl</code> [<var>options</var>] <var>command</var> [<var>arg</var>...]</p> + + <h1>Description</h1> + <p>This utility can be used to manage the OVN interconnection southbound database.</p> + + <h1>General Commands</h1> + + <dl> + <dt><code>init</code></dt> + <dd> + Initializes the database, if it is empty. If the database has already + been initialized, this command has no effect. + </dd> + + <dt><code>show [<var>availability_zone</var>]</code></dt> + <dd> + Prints a brief overview of the database contents. If + <var>availability_zone</var> is provided, only records related to that + availability zone are shown. + </dd> + </dl> + + <h1>Database Commands</h1> + <p>These commands query and modify the contents of <code>ovsdb</code> tables. + They are a slight abstraction of the <code>ovsdb</code> interface and + as such they operate at a lower level than other <code>ovn-isbctl</code> commands.</p> + <p><var>Identifying Tables, Records, and Columns</var></p> + <p>Each of these commands has a <var>table</var> parameter to identify a table + within the database. Many of them also take a <var>record</var> parameter + that identifies a particular record within a table. The <var>record</var> + parameter may be the UUID for a record, which may be abbreviated to its + first 4 (or more) hex digits, as long as that is unique. Many tables offer + additional ways to identify records. Some commands also take + <var>column</var> parameters that identify a particular field within the + records in a table.</p> + + <p> + For a list of tables and their columns, see <code>ovn-isb</code>(5) or + see the table listing from the <code>--help</code> option. + </p> + + <p> + Record names must be specified in full and with correct capitalization, + except that UUIDs may be abbreviated to their first 4 (or more) hex + digits, as long as that is unique within the table. Names of tables and + columns are not case-sensitive, and <code>-</code> and <code>_</code> are + treated interchangeably. Unique abbreviations of table and column names + are acceptable, e.g. <code>g</code> or <code>gatew</code> is sufficient + to identify the <code>Gateway</code> table. + </p> + + <xi:include href="lib/db-ctl-base.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> + + <h1>Remote Connectivity Commands</h1> + <dl> + <dt><code>get-connection</code></dt> + <dd> + Prints the configured connection(s). + </dd> + + <dt><code>del-connection</code></dt> + <dd> + Deletes the configured connection(s). + </dd> + + <dt>[<code>--inactivity-probe=</code><var>msecs</var>] <code>set-connection</code> <var>target</var>...</dt> + <dd> + Sets the configured manager target or targets. Use + <code>--inactivity-probe=</code><var>msecs</var> to override the default + idle connection inactivity probe time. Use 0 to disable inactivity probes. + </dd> + </dl> + + <h1>SSL Configuration Commands</h1> + <dl> + <dt><code>get-ssl</code></dt> + <dd> + Prints the SSL configuration. + </dd> + + <dt><code>del-ssl</code></dt> + <dd> + Deletes the current SSL configuration. + </dd> + + <dt>[<code>--bootstrap</code>] <code>set-ssl</code> + <var>private-key</var> <var>certificate</var> <var>ca-cert</var> + [<var>ssl-protocol-list</var> [<var>ssl-cipher-list</var>]]</dt> + <dd> + Sets the SSL configuration. + </dd> + </dl> + + <h1>Options</h1> + + <dl> + <dt><code>--db</code> <var>database</var></dt> + <dd> + The OVSDB database remote to contact. If the <env>OVN_ISB_DB</env> + environment variable is set, its value is used as the default. + Otherwise, the default is <code>unix:@RUNDIR@/ovnisb_db.sock</code>, but this + default is unlikely to be useful outside of single-machine OVN test + environments. + </dd> + + <dt><code>--leader-only</code></dt> + <dt><code>--no-leader-only</code></dt> + <dd> + By default, or with <code>--leader-only</code>, when the database server + is a clustered database, <code>ovn-isbctl</code> will avoid servers other + than the cluster leader. This ensures that any data that + <code>ovn-isbctl</code> reads and reports is up-to-date. With + <code>--no-leader-only</code>, <code>ovn-isbctl</code> will use any server + in the cluster, which means that for read-only transactions it can report + and act on stale data (transactions that modify the database are always + serialized even with <code>--no-leader-only</code>). Refer to + <code>Understanding Cluster Consistency</code> in <code>ovsdb</code>(7) + for more information. + </dd> + </dl> + + <h1>Logging options</h1> + <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> + + <h1>Table Formatting Options</h1> + These options control the format of output from the <code>list</code> and + <code>find</code> commands. + <xi:include href="lib/table.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> + + <h2>PKI Options</h2> + <p> + PKI configuration is required to use SSL for the connection to the + database. + </p> + <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> + <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> + + <h2>Other Options</h2> + + <xi:include href="lib/common.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/> + +</manpage> diff --git a/utilities/ovn-isbctl.c b/utilities/ovn-isbctl.c new file mode 100644 index 0000000..9ac3a4b --- /dev/null +++ b/utilities/ovn-isbctl.c @@ -0,0 +1,1015 @@ +/* + * 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 <config.h> + +#include <ctype.h> +#include <getopt.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdlib.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/shash.h" +#include "openvswitch/vconn.h" +#include "openvswitch/vlog.h" +#include "lib/ovn-isb-idl.h" +#include "lib/ovn-util.h" +#include "openvswitch/poll-loop.h" +#include "process.h" +#include "sset.h" +#include "stream-ssl.h" +#include "stream.h" +#include "table.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, true, false); + 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: + ovs_abort(0, "Internal error when parsing option %d.", c); + + 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. + * Now empty, just keep the framework for future additions. */ +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; +}; + +static void +isbctl_init(struct ctl_context *ctx OVS_UNUSED) +{ +} + +static char * +az_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist, + const struct isbrec_availability_zone **az_p) +{ + const struct isbrec_availability_zone *az = NULL; + *az_p = NULL; + + struct uuid az_uuid; + bool is_uuid = uuid_from_string(&az_uuid, id); + if (is_uuid) { + az = isbrec_availability_zone_get_for_uuid(ctx->idl, &az_uuid); + } + + if (!az) { + const struct isbrec_availability_zone *iter; + + ISBREC_AVAILABILITY_ZONE_FOR_EACH (iter, ctx->idl) { + if (!strcmp(iter->name, id)) { + az = iter; + break; + } + } + } + + if (!az && must_exist) { + return xasprintf("%s: availability zone %s not found", + id, is_uuid ? "UUID" : "name"); + } + + *az_p = az; + return NULL; +} + +/* Data structures for the show command, which constructs the hierarchy + * of availability_zone:gateway:port_binding. */ +struct gw_data { + const struct isbrec_gateway *isb_gw; + /* With node type struct isbrec_port_binding. + * We don't need to search port-binding, but using shash makes + * sorting convenient. */ + struct shash pbs; +}; + +struct az_data { + const struct isbrec_availability_zone *isb_az; + /* With node type struct gw_data */ + struct shash gws; +}; + +static void +init_az_info(struct ctl_context *ctx, struct shash *azs) +{ + const struct isbrec_availability_zone *az; + ISBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->idl) { + struct az_data *az_data = xmalloc(sizeof *az_data); + shash_init(&az_data->gws); + az_data->isb_az = az; + shash_add(azs, az->name, az_data); + } + + const struct isbrec_gateway *gw; + ISBREC_GATEWAY_FOR_EACH (gw, ctx->idl) { + struct az_data *az_data = shash_find_data(azs, + gw->availability_zone->name); + ovs_assert(az_data); + struct gw_data *gw_data = xmalloc(sizeof *gw_data); + gw_data->isb_gw = gw; + shash_init(&gw_data->pbs); + shash_add(&az_data->gws, gw->name, gw_data); + } + + const struct isbrec_port_binding *pb; + ISBREC_PORT_BINDING_FOR_EACH (pb, ctx->idl) { + struct az_data *az_data = shash_find_data(azs, + pb->availability_zone->name); + ovs_assert(az_data); + struct gw_data *gw_data = shash_find_data(&az_data->gws, pb->gateway); + if (!gw_data) { + continue; + } + shash_add(&gw_data->pbs, pb->logical_port, pb); + } +} + +static void +destroy_az_info(struct shash *azs) +{ + struct shash_node *az_node; + SHASH_FOR_EACH (az_node, azs) { + struct az_data *az_data = az_node->data; + struct shash_node *gw_node; + SHASH_FOR_EACH (gw_node, &az_data->gws) { + struct gw_data *gw_data = gw_node->data; + shash_destroy(&gw_data->pbs); + free(gw_data); + } + shash_destroy(&az_data->gws); + free(az_data); + } + shash_destroy(azs); +} + +static void +print_az(struct az_data *az, struct ds *s) +{ + ds_put_format(s, "availability-zone %s\n", az->isb_az->name); + + const struct shash_node **nodes = shash_sort(&az->gws); + for (int g = 0; g < shash_count(&az->gws); g++) { + struct gw_data *gw = nodes[g]->data; + + ds_put_format(s, " gateway %s\n", gw->isb_gw->name); + if (gw->isb_gw->hostname[0]) { + ds_put_format(s, " hostname: %s\n", gw->isb_gw->hostname); + } + + for (int i = 0; i < gw->isb_gw->n_encaps; i++) { + ds_put_format(s, " type: %s\n", + gw->isb_gw->encaps[i]->type); + ds_put_format(s, " ip: %s\n", + gw->isb_gw->encaps[i]->ip); + } + + const struct shash_node **pb_nodes = shash_sort(&gw->pbs); + for (int p = 0; p < shash_count(&gw->pbs); p++) { + const struct isbrec_port_binding *pb = pb_nodes[p]->data; + ds_put_format(s, " port %s\n", + pb->logical_port); + ds_put_format(s, " transit switch: %s\n", + pb->transit_switch); + ds_put_format(s, " address: [\"%s\"]\n", + pb->address); + } + free(pb_nodes); + } + free(nodes); +} + +static void +isbctl_show(struct ctl_context *ctx) +{ + /* Availability zones with the gateways and port-bindings data. Node + * type is struct az_data. */ + struct shash azs = SHASH_INITIALIZER(&azs); + init_az_info(ctx, &azs); + + const struct isbrec_availability_zone *az; + + if (ctx->argc == 2) { + char *error = az_by_name_or_uuid(ctx, ctx->argv[1], false, &az); + if (error) { + ctx->error = error; + goto error; + } + if (az) { + struct az_data *az_data = shash_find_data(&azs, az->name); + ovs_assert(az_data); + print_az(az_data, &ctx->output); + } + } else { + const struct shash_node **nodes = shash_sort(&azs); + for (int i = 0; i < shash_count(&azs); i++) { + struct az_data *az_data = nodes[i]->data; + print_az(az_data, &ctx->output); + } + free(nodes); + } + +error: + destroy_az_info(&azs); +} + +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}, + + [ISBREC_TABLE_GATEWAY].row_ids[0] = + {&isbrec_gateway_col_name, NULL, NULL}, + + [ISBREC_TABLE_PORT_BINDING].row_ids[0] = + {&isbrec_port_binding_col_logical_port, NULL, NULL}, + + [ISBREC_TABLE_DATAPATH_BINDING].row_ids[0] = + {&isbrec_datapath_binding_col_transit_switch, 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, NULL); + 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 }, + { "show", 0, 1, "[AZ]", NULL, isbctl_show, NULL, "", RO }, + + /* 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, + NULL, isbctl_exit); + ctl_register_commands(isbctl_commands); +}
This patch introduces OVN_IC_Southbound DB schema and the CLI ovn-isbctl that manages the DB. Signed-off-by: Han Zhou <hzhou@ovn.org> --- .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 | 129 ++++++ ovn-isb.xml | 582 +++++++++++++++++++++++++ tests/automake.mk | 2 + tests/ovn-isbctl.at | 112 +++++ tests/testsuite.at | 1 + utilities/.gitignore | 2 + utilities/automake.mk | 8 + utilities/ovn-isbctl.8.xml | 148 +++++++ utilities/ovn-isbctl.c | 1015 ++++++++++++++++++++++++++++++++++++++++++++ 18 files changed, 2085 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 tests/ovn-isbctl.at create mode 100644 utilities/ovn-isbctl.8.xml create mode 100644 utilities/ovn-isbctl.c