diff mbox series

[ovs-dev,v4] ovsdb idl: Add the support to specify the uuid for row insert.

Message ID 20220629165720.3885265-1-numans@ovn.org
State Changes Requested
Headers show
Series [ovs-dev,v4] ovsdb idl: Add the support to specify the uuid for row insert. | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Numan Siddique June 29, 2022, 4:57 p.m. UTC
From: Numan Siddique <numans@ovn.org>

ovsdb-server allows the OVSDB clients to specify the uuid for
the row inserts [1].  The C IDL client library is  missing this
feature.  This patch adds this support.

For each schema table, a new function is generated -
<schema_table>insert_persistent_uuid(txn, uuid) and the users
of IDL client library can make use of this function.

ovs-vsctl and other derivatives of ctl now supports the same
in the generic 'create' command with the option "--id=<UUID>".

[1] - a529e3cd1f("ovsdb-server: Allow OVSDB clients to specify the UUID for inserted rows.:)

Signed-off-by: Numan Siddique <numans@ovn.org>
Acked-by: Adrian Moreno <amorenoz@redhat.com>
Acked-by: Han Zhou <hzhou@ovn.org>
---

v3 -> v4
---
  * Added an entry in python/TODO.rst.

v2 -> v3
----
  * Addressed review comments from Han
      - Added test case for --id ctl option

v1 -> v2
-----
  * Addressed review comments from Adrian Moreno
  * Added the support in generic 'create' command to specify the uuid in
    --id option.


 lib/db-ctl-base.c        | 38 ++++++++++++------
 lib/db-ctl-base.man      |  5 ++-
 lib/db-ctl-base.xml      |  4 ++
 lib/ovsdb-idl-provider.h |  1 +
 lib/ovsdb-idl.c          | 86 +++++++++++++++++++++++++++++-----------
 lib/ovsdb-idl.h          |  3 ++
 ovsdb/ovsdb-idlc.in      | 15 +++++++
 python/TODO.rst          |  2 +
 tests/ovs-vsctl.at       | 25 ++++++++++++
 tests/ovsdb-idl.at       | 27 +++++++++++++
 tests/test-ovsdb.c       | 59 +++++++++++++++++++++++++++
 11 files changed, 229 insertions(+), 36 deletions(-)

Comments

0-day Robot June 29, 2022, 5:21 p.m. UTC | #1
Bleep bloop.  Greetings Numan Siddique, 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 118 characters long (recommended limit is 79)
#104 FILE: lib/db-ctl-base.man:206:
.IP "[\fB\-\-id=@\fIname\fR or \fB\-\-id=\fIuuid\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."

Lines checked: 469, Warnings: 1, Errors: 0


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
Ilya Maximets July 12, 2022, 11 a.m. UTC | #2
On 6/29/22 18:57, numans@ovn.org wrote:
> From: Numan Siddique <numans@ovn.org>
> 
> ovsdb-server allows the OVSDB clients to specify the uuid for
> the row inserts [1].  The C IDL client library is  missing this
> feature.  This patch adds this support.
> 
> For each schema table, a new function is generated -
> <schema_table>insert_persistent_uuid(txn, uuid) and the users
> of IDL client library can make use of this function.
> 
> ovs-vsctl and other derivatives of ctl now supports the same
> in the generic 'create' command with the option "--id=<UUID>".
> 
> [1] - a529e3cd1f("ovsdb-server: Allow OVSDB clients to specify the UUID for inserted rows.:)
> 
> Signed-off-by: Numan Siddique <numans@ovn.org>
> Acked-by: Adrian Moreno <amorenoz@redhat.com>
> Acked-by: Han Zhou <hzhou@ovn.org>
> ---

Hi, Numan.  Thanks for the patch.  It looks OK in general,
see some comments inline.

> 
> v3 -> v4
> ---
>   * Added an entry in python/TODO.rst.
> 
> v2 -> v3
> ----
>   * Addressed review comments from Han
>       - Added test case for --id ctl option
> 
> v1 -> v2
> -----
>   * Addressed review comments from Adrian Moreno
>   * Added the support in generic 'create' command to specify the uuid in
>     --id option.
> 
> 
>  lib/db-ctl-base.c        | 38 ++++++++++++------
>  lib/db-ctl-base.man      |  5 ++-
>  lib/db-ctl-base.xml      |  4 ++
>  lib/ovsdb-idl-provider.h |  1 +
>  lib/ovsdb-idl.c          | 86 +++++++++++++++++++++++++++++-----------
>  lib/ovsdb-idl.h          |  3 ++
>  ovsdb/ovsdb-idlc.in      | 15 +++++++
>  python/TODO.rst          |  2 +
>  tests/ovs-vsctl.at       | 25 ++++++++++++
>  tests/ovsdb-idl.at       | 27 +++++++++++++
>  tests/test-ovsdb.c       | 59 +++++++++++++++++++++++++++
>  11 files changed, 229 insertions(+), 36 deletions(-)

We need a NEWS entry for this.

> 
> diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
> index 707456158..f39e090a0 100644
> --- a/lib/db-ctl-base.c
> +++ b/lib/db-ctl-base.c
> @@ -1732,28 +1732,42 @@ cmd_create(struct ctl_context *ctx)
>      const struct ovsdb_idl_row *row;
>      const struct uuid *uuid = NULL;
>      int i;
> +    struct uuid uuid_;
> +    bool persist_uuid = false;

Reverse x-mass tree, please.

>  
>      ctx->error = get_table(table_name, &table);
>      if (ctx->error) {
>          return;
>      }
> +
>      if (id) {
> -        struct ovsdb_symbol *symbol = NULL;
> +        if (uuid_from_string(&uuid_, id)) {
> +            uuid = &uuid_;
> +            persist_uuid = true;
> +        } else {
> +            struct ovsdb_symbol *symbol = NULL;
>  
> -        ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
> -        if (ctx->error) {
> -            return;
> -        }
> -        if (table->is_root) {
> -            /* This table is in the root set, meaning that rows created in it
> -             * won't disappear even if they are unreferenced, so disable
> -             * warnings about that by pretending that there is a reference. */
> -            symbol->strong_ref = true;
> +            ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
> +            if (ctx->error) {
> +                return;
> +            }
> +            if (table->is_root) {
> +                /* This table is in the root set, meaning that rows created in
> +                * it won't disappear even if they are unreferenced, so disable
> +                * warnings about that by pretending that there is a
> +                * reference. */

Subsequent lines of a comment block are 1 spaece off.

> +                symbol->strong_ref = true;
> +            }
> +            uuid = &symbol->uuid;
>          }
> -        uuid = &symbol->uuid;
>      }
>  
> -    row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
> +    if (persist_uuid) {
> +        row = ovsdb_idl_txn_insert_persist_uuid(ctx->txn, table, uuid);
> +    } else {
> +        row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
> +    }
> +
>      for (i = 2; i < ctx->argc; i++) {
>          ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
>          if (ctx->error) {
> diff --git a/lib/db-ctl-base.man b/lib/db-ctl-base.man
> index 9c786f298..4dc165f94 100644
> --- a/lib/db-ctl-base.man
> +++ b/lib/db-ctl-base.man
> @@ -203,7 +203,7 @@ Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not
>  exist.  With \fB\-\-if-exists\fR, this command does nothing if
>  \fIrecord\fR does not exist.
>  .
> -.IP "[\fB\-\-id=@\fIname\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
> +.IP "[\fB\-\-id=@\fIname\fR or \fB\-\-id=\fIuuid\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."

I think, it should be [--id=(@name|uuid)]

>  Creates a new record in \fItable\fR and sets the initial values of
>  each \fIcolumn\fR.  Columns not explicitly set will receive their
>  default values.  Outputs the UUID of the new row.
> @@ -212,6 +212,9 @@ If \fB@\fIname\fR is specified, then the UUID for the new row may be
>  referred to by that name elsewhere in the same \fB\*(PN\fR
>  invocation in contexts where a UUID is expected.  Such references may
>  precede or follow the \fBcreate\fR command.
> +.IP
> +If a valid \fIuuid\fR is specified, then it is used as the UUID
> +of the new row.
>  .
>  .RS
>  .IP "Caution (ovs-vsctl as example)"
> diff --git a/lib/db-ctl-base.xml b/lib/db-ctl-base.xml
> index f6efe98ea..4f63aa7f7 100644
> --- a/lib/db-ctl-base.xml
> +++ b/lib/db-ctl-base.xml
> @@ -323,6 +323,10 @@

The argument line needs an update in this file as well.

>          invocation in contexts where a UUID is expected.  Such references may
>          precede or follow the <code>create</code> command.
>        </p>
> +      <p>
> +        If a valid <var>uuid</var> is specified, then it is used as the
> +        UUID of the new row.
> +      </p>
>        <dl>
>          <dt>Caution (ovs-vsctl as example)</dt>
>          <dd>
> diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
> index 8797686f9..332c4075b 100644
> --- a/lib/ovsdb-idl-provider.h
> +++ b/lib/ovsdb-idl-provider.h
> @@ -74,6 +74,7 @@ struct ovsdb_idl_row {
>      struct ovs_list dst_arcs;   /* Backward arcs (ovsdb_idl_arc.dst_node). */
>      struct ovsdb_idl_table *table; /* Containing table. */
>      struct ovsdb_datum *old_datum; /* Committed data (null if orphaned). */
> +    bool persist_uuid;          /* persist 'uuid' during insert txn if set. */

Comments should start with a capital letter.

>      bool parsed; /* Whether the row is parsed. */
>      struct ovs_list reparse_node; /* Rows that needs to be re-parsed due to
>                                     * insertion of a referenced row. */
> diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
> index 3b8741408..9ddb49273 100644
> --- a/lib/ovsdb-idl.c
> +++ b/lib/ovsdb-idl.c
> @@ -2849,11 +2849,14 @@ substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
>  
>              row = ovsdb_idl_txn_get_row(txn, &uuid);
>              if (row && !row->old_datum && row->new_datum) {
> -                json_destroy(json);
> -
> -                return json_array_create_2(
> -                    json_string_create("named-uuid"),
> -                    json_string_create_nocopy(ovsdb_data_row_name(&uuid)));
> +                if (row->persist_uuid) {
> +                    return json;
> +                } else {
> +                    json_destroy(json);
> +                    return json_array_create_2(
> +                        json_string_create("named-uuid"),
> +                        json_string_create_nocopy(ovsdb_data_row_name(&uuid)));
> +                }
>              }
>          }
>  
> @@ -3278,9 +3281,19 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
>  
>                  any_updates = true;
>  
> -                json_object_put(op, "uuid-name",
> -                                json_string_create_nocopy(
> -                                    ovsdb_data_row_name(&row->uuid)));
> +                char *uuid_json;
> +                struct json *value;
> +                if (row->persist_uuid) {
> +                    uuid_json = "uuid";
> +                    value = json_string_create_nocopy(
> +                        xasprintf(UUID_FMT, UUID_ARGS(&row->uuid)));
> +                } else {
> +                    uuid_json = "uuid-name";
> +                    value = json_string_create_nocopy(
> +                                ovsdb_data_row_name(&row->uuid));
> +                }
> +
> +                json_object_put(op, uuid_json, value);
>  
>                  insert = xmalloc(sizeof *insert);
>                  insert->dummy = row->uuid;
> @@ -3765,6 +3778,32 @@ ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
>      row->new_datum = NULL;
>  }
>  
> +static const struct ovsdb_idl_row *
> +ovsdb_idl_txn_insert__(struct ovsdb_idl_txn *txn,
> +                       const struct ovsdb_idl_table_class *class,
> +                       const struct uuid *uuid,
> +                       bool persist_uuid)
> +{
> +    struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
> +

Should we also ovs_assert(uuid || !persist_uuid) ?

> +    if (uuid) {
> +        ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
> +        row->uuid = *uuid;
> +        row->persist_uuid = persist_uuid;
> +    } else {
> +        uuid_generate(&row->uuid);
> +        row->persist_uuid = false;
> +    }
> +
> +    row->table = ovsdb_idl_table_from_class(txn->idl, class);
> +    row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
> +    hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
> +    hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
> +    ovsdb_idl_add_to_indexes(row);
> +
> +    return row;
> +}
> +
>  /* Inserts and returns a new row in the table with the specified 'class' in the
>   * database with open transaction 'txn'.
>   *
> @@ -3782,22 +3821,23 @@ ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn,
>                       const struct ovsdb_idl_table_class *class,
>                       const struct uuid *uuid)
>  {
> -    struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
> -
> -    if (uuid) {
> -        ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
> -        row->uuid = *uuid;
> -    } else {
> -        uuid_generate(&row->uuid);
> -    }
> -
> -    row->table = ovsdb_idl_table_from_class(txn->idl, class);
> -    row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
> -    hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
> -    hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
> -    ovsdb_idl_add_to_indexes(row);
> +    return ovsdb_idl_txn_insert__(txn, class, uuid, false);
> +}
>  
> -    return row;
> +/* Inserts and returns a new row in the table with the specified 'class' in the
> + * database with open transaction 'txn'.
> + *
> + * The new row is assigned the specified UUID (which cannot be null).
> + *
> + * Usually this function is used indirectly through one of the
> + * "insert_persist_uuid" functions generated by ovsdb-idlc. */
> +const struct ovsdb_idl_row *
> +ovsdb_idl_txn_insert_persist_uuid(struct ovsdb_idl_txn *txn,
> +                                  const struct ovsdb_idl_table_class *class,
> +                                  const struct uuid *uuid)
> +{
> +    ovs_assert(uuid);
> +    return ovsdb_idl_txn_insert__(txn, class, uuid, true);
>  }
>  
>  static void
> diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
> index fbd9f671a..9a3e19f20 100644
> --- a/lib/ovsdb-idl.h
> +++ b/lib/ovsdb-idl.h
> @@ -375,6 +375,9 @@ void ovsdb_idl_txn_delete(const struct ovsdb_idl_row *);
>  const struct ovsdb_idl_row *ovsdb_idl_txn_insert(
>      struct ovsdb_idl_txn *, const struct ovsdb_idl_table_class *,
>      const struct uuid *);
> +const struct ovsdb_idl_row *ovsdb_idl_txn_insert_persist_uuid(
> +    struct ovsdb_idl_txn *txn, const struct ovsdb_idl_table_class *class,
> +    const struct uuid *uuid);
>  
>  struct ovsdb_idl *ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *);
>  void ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *);
> diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
> index 13c535939..7e09497ea 100755
> --- a/ovsdb/ovsdb-idlc.in
> +++ b/ovsdb/ovsdb-idlc.in
> @@ -362,6 +362,8 @@ struct %(s)s *%(s)s_cursor_data(struct ovsdb_idl_cursor *);
>  void %(s)s_init(struct %(s)s *);
>  void %(s)s_delete(const struct %(s)s *);
>  struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
> +struct %(s)s *%(s)s_insert_persist_uuid(
> +    struct ovsdb_idl_txn *txn, const struct uuid *uuid);
>  
>  /* Returns true if the tracked column referenced by 'enum %(s)s_column_id' of
>   * the row referenced by 'struct %(s)s *' was updated since the last change
> @@ -809,6 +811,19 @@ struct %(s)s *
>      return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_%(tl)s, NULL));
>  }
>  
> +/* Inserts and returns a new row in the table "%(t)s" in the database
> + * with open transaction 'txn'.
> + *
> + * The new row is assigned the UUID specified in the 'uuid' parameter
> + * (which cannot be null).  ovsdb-server will try to assign the same
> + * UUID when 'txn' is committed. */
> +struct %(s)s *
> +%(s)s_insert_persist_uuid(struct ovsdb_idl_txn *txn, const struct uuid *uuid)
> +{
> +    return %(s)s_cast(ovsdb_idl_txn_insert_persist_uuid(
> +        txn, &%(p)stable_%(tl)s, uuid));
> +}
> +
>  bool
>  %(s)s_is_updated(const struct %(s)s *row, enum %(s)s_column_id column)
>  {
> diff --git a/python/TODO.rst b/python/TODO.rst
> index 3a53489f1..a0b3e807a 100644
> --- a/python/TODO.rst
> +++ b/python/TODO.rst
> @@ -32,3 +32,5 @@ Python Bindings To-do List
>  
>    * Support write-only-changed monitor mode (equivalent of
>      OVSDB_IDL_WRITE_CHANGED_ONLY).
> +
> +  * Support accepting the uuid for row inserts.
> diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
> index d6cd2c084..abf4fb9cf 100644
> --- a/tests/ovs-vsctl.at
> +++ b/tests/ovs-vsctl.at
> @@ -1710,3 +1710,28 @@ ingress_policing_kpkts_rate: 100
>  ])
>  OVS_VSCTL_CLEANUP
>  AT_CLEANUP
> +
> +AT_SETUP([ovs-vsctl create bridge with uuid])
> +AT_KEYWORDS([create bridge with uuid])
> +OVS_VSCTL_SETUP
> +
> +AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \
> +name=tst0 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [0],[dnl
> +c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> +])
> +
> +AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \
> +name=tst1 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [1], [ignore], [ignore])
> +
> +AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge], [0], [dnl
> +c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> +tst0
> +])
> +
> +ovs-vsctl --no-wait --id=@a create bridge \
> +name=tst1 -- add open . bridges @a
> +
> +AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge tst1], [0], [ignore])
> +
> +OVS_VSCTL_CLEANUP
> +AT_CLEANUP
> diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
> index d1cfa59c5..28c032105 100644
> --- a/tests/ovsdb-idl.at
> +++ b/tests/ovsdb-idl.at
> @@ -2466,3 +2466,30 @@ unix:socket2 remote has col id in table simple7
>  
>  OVSDB_SERVER_SHUTDOWN
>  AT_CLEANUP
> +
> +AT_SETUP([idl creating rows with persistent uuid - C])
> +AT_KEYWORDS([ovsdb client idl txn])
> +
> +AT_CHECK([ovsdb_start_idltest "" "$abs_srcdir/idltest.ovsschema"])
> +AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl-txn-persistent-uuid unix:socket],
> +         [0], [stdout], [stderr])
> +AT_CHECK([sort stdout], [0],
> +    [[000: After inserting simple, simple3 and simple4
> +001: table simple3: name=simple3 uset=[] uref=[c5cc12f8-eaa1-43a7-8a73-bccd18df1111] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222
> +001: table simple4: name=simple4 uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> +001: table simple: i=0 r=0 b=false s=simple u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333
> +002: After inserting simple with same uuid
> +003: table simple3: name=simple3 uset=[] uref=[c5cc12f8-eaa1-43a7-8a73-bccd18df1111] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222
> +003: table simple4: name=simple4 uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> +003: table simple: i=0 r=0 b=false s=simple u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333
> +004: End test
> +]])
> +
> +AT_CHECK([grep ovsdb_idl stderr | sort], [0], [dnl
> +test-ovsdb|ovsdb_idl|transaction error: {"details":"This UUID would dnl
> +duplicate a UUID already present within the table or deleted within dnl
> +the same transaction.","error":"duplicate uuid","syntax":"\"c5cc12f8-eaa1-43a7-8a73-bccd18df3333\""}
> +])
> +
> +OVSDB_SERVER_SHUTDOWN
> +AT_CLEANUP
> diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
> index 0fef1f978..515a3015d 100644
> --- a/tests/test-ovsdb.c
> +++ b/tests/test-ovsdb.c
> @@ -3389,6 +3389,63 @@ do_idl_table_column_check(struct ovs_cmdl_context *ctx)
>      ovsdb_idl_destroy(idl);
>  }
>  
> +static void
> +do_idl_txn_persistent_uuid(struct ovs_cmdl_context *ctx)
> +{
> +    struct ovsdb_idl *idl;
> +    struct ovsdb_idl_txn *myTxn;
> +    int step = 0;
> +
> +    idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, true, true);
> +    ovsdb_idl_get_initial_snapshot(idl);
> +    ovsdb_idl_run(idl);
> +
> +    myTxn = ovsdb_idl_txn_create(idl);
> +
> +    struct uuid uuid1;
> +    uuid_from_string(&uuid1, "c5cc12f8-eaa1-43a7-8a73-bccd18df1111");
> +
> +    struct uuid uuid2;
> +    uuid_from_string(&uuid2, "c5cc12f8-eaa1-43a7-8a73-bccd18df2222");
> +
> +    struct idltest_simple4 *simple4_row =
> +        idltest_simple4_insert_persist_uuid(myTxn, &uuid1);
> +    idltest_simple4_set_name(simple4_row, "simple4");
> +
> +    struct idltest_simple3 *simple3_row =
> +        idltest_simple3_insert_persist_uuid(myTxn, &uuid2);
> +    idltest_simple3_set_name(simple3_row, "simple3");
> +    idltest_simple3_set_uref(simple3_row, &simple4_row, 1);
> +
> +    struct uuid uuid3;
> +    uuid_from_string(&uuid3, "c5cc12f8-eaa1-43a7-8a73-bccd18df3333");
> +
> +    struct idltest_simple *simple_row =
> +        idltest_simple_insert_persist_uuid(myTxn, &uuid3);
> +    idltest_simple_set_s(simple_row, "simple");
> +
> +    ovsdb_idl_txn_commit_block(myTxn);
> +    ovsdb_idl_txn_destroy(myTxn);
> +    ovsdb_idl_get_initial_snapshot(idl);
> +    printf("%03d: After inserting simple, simple3 and simple4\n", step++);
> +    print_idl(idl, step++, false);
> +
> +    /* Create another txn, insert the row in simple table with the existing
> +     * uuid. */
> +    myTxn = ovsdb_idl_txn_create(idl);
> +    simple_row =
> +        idltest_simple_insert_persist_uuid(myTxn, &uuid3);
> +    idltest_simple_set_s(simple_row, "simple_foo");
> +    ovsdb_idl_txn_commit_block(myTxn);
> +    ovsdb_idl_txn_destroy(myTxn);
> +    ovsdb_idl_get_initial_snapshot(idl);
> +    printf("%03d: After inserting simple with same uuid\n", step++);
> +    print_idl(idl, step++, false);
> +
> +    ovsdb_idl_destroy(idl);
> +    printf("%03d: End test\n", step++);
> +}
> +
>  static struct ovs_cmdl_command all_commands[] = {
>      { "log-io", NULL, 2, INT_MAX, do_log_io, OVS_RO },
>      { "default-atoms", NULL, 0, 0, do_default_atoms, OVS_RO },
> @@ -3429,6 +3486,8 @@ static struct ovs_cmdl_command all_commands[] = {
>          do_idl_partial_update_set_column, OVS_RO },
>      { "idl-table-column-check", NULL, 2, 2,
>          do_idl_table_column_check, OVS_RO },
> +    { "idl-txn-persistent-uuid", NULL, 1, INT_MAX,
> +        do_idl_txn_persistent_uuid, OVS_RO },
>      { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
>      { NULL, NULL, 0, 0, NULL, OVS_RO },
>  };
Numan Siddique July 18, 2022, 5:39 p.m. UTC | #3
On Tue, Jul 12, 2022 at 6:02 AM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> On 6/29/22 18:57, numans@ovn.org wrote:
> > From: Numan Siddique <numans@ovn.org>
> >
> > ovsdb-server allows the OVSDB clients to specify the uuid for
> > the row inserts [1].  The C IDL client library is  missing this
> > feature.  This patch adds this support.
> >
> > For each schema table, a new function is generated -
> > <schema_table>insert_persistent_uuid(txn, uuid) and the users
> > of IDL client library can make use of this function.
> >
> > ovs-vsctl and other derivatives of ctl now supports the same
> > in the generic 'create' command with the option "--id=<UUID>".
> >
> > [1] - a529e3cd1f("ovsdb-server: Allow OVSDB clients to specify the UUID for inserted rows.:)
> >
> > Signed-off-by: Numan Siddique <numans@ovn.org>
> > Acked-by: Adrian Moreno <amorenoz@redhat.com>
> > Acked-by: Han Zhou <hzhou@ovn.org>
> > ---
>
> Hi, Numan.  Thanks for the patch.  It looks OK in general,
> see some comments inline.

Thanks for the review comments.


>
> >
> > v3 -> v4
> > ---
> >   * Added an entry in python/TODO.rst.
> >
> > v2 -> v3
> > ----
> >   * Addressed review comments from Han
> >       - Added test case for --id ctl option
> >
> > v1 -> v2
> > -----
> >   * Addressed review comments from Adrian Moreno
> >   * Added the support in generic 'create' command to specify the uuid in
> >     --id option.
> >
> >
> >  lib/db-ctl-base.c        | 38 ++++++++++++------
> >  lib/db-ctl-base.man      |  5 ++-
> >  lib/db-ctl-base.xml      |  4 ++
> >  lib/ovsdb-idl-provider.h |  1 +
> >  lib/ovsdb-idl.c          | 86 +++++++++++++++++++++++++++++-----------
> >  lib/ovsdb-idl.h          |  3 ++
> >  ovsdb/ovsdb-idlc.in      | 15 +++++++
> >  python/TODO.rst          |  2 +
> >  tests/ovs-vsctl.at       | 25 ++++++++++++
> >  tests/ovsdb-idl.at       | 27 +++++++++++++
> >  tests/test-ovsdb.c       | 59 +++++++++++++++++++++++++++
> >  11 files changed, 229 insertions(+), 36 deletions(-)
>
> We need a NEWS entry for this.
>
> >
> > diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
> > index 707456158..f39e090a0 100644
> > --- a/lib/db-ctl-base.c
> > +++ b/lib/db-ctl-base.c
> > @@ -1732,28 +1732,42 @@ cmd_create(struct ctl_context *ctx)
> >      const struct ovsdb_idl_row *row;
> >      const struct uuid *uuid = NULL;
> >      int i;
> > +    struct uuid uuid_;
> > +    bool persist_uuid = false;
>
> Reverse x-mass tree, please.

Done in v5

>
> >
> >      ctx->error = get_table(table_name, &table);
> >      if (ctx->error) {
> >          return;
> >      }
> > +
> >      if (id) {
> > -        struct ovsdb_symbol *symbol = NULL;
> > +        if (uuid_from_string(&uuid_, id)) {
> > +            uuid = &uuid_;
> > +            persist_uuid = true;
> > +        } else {
> > +            struct ovsdb_symbol *symbol = NULL;
> >
> > -        ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
> > -        if (ctx->error) {
> > -            return;
> > -        }
> > -        if (table->is_root) {
> > -            /* This table is in the root set, meaning that rows created in it
> > -             * won't disappear even if they are unreferenced, so disable
> > -             * warnings about that by pretending that there is a reference. */
> > -            symbol->strong_ref = true;
> > +            ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
> > +            if (ctx->error) {
> > +                return;
> > +            }
> > +            if (table->is_root) {
> > +                /* This table is in the root set, meaning that rows created in
> > +                * it won't disappear even if they are unreferenced, so disable
> > +                * warnings about that by pretending that there is a
> > +                * reference. */
>
> Subsequent lines of a comment block are 1 spaece off.
Done in v5.

>
> > +                symbol->strong_ref = true;
> > +            }
> > +            uuid = &symbol->uuid;
> >          }
> > -        uuid = &symbol->uuid;
> >      }
> >
> > -    row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
> > +    if (persist_uuid) {
> > +        row = ovsdb_idl_txn_insert_persist_uuid(ctx->txn, table, uuid);
> > +    } else {
> > +        row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
> > +    }
> > +
> >      for (i = 2; i < ctx->argc; i++) {
> >          ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
> >          if (ctx->error) {
> > diff --git a/lib/db-ctl-base.man b/lib/db-ctl-base.man
> > index 9c786f298..4dc165f94 100644
> > --- a/lib/db-ctl-base.man
> > +++ b/lib/db-ctl-base.man
> > @@ -203,7 +203,7 @@ Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not
> >  exist.  With \fB\-\-if-exists\fR, this command does nothing if
> >  \fIrecord\fR does not exist.
> >  .
> > -.IP "[\fB\-\-id=@\fIname\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
> > +.IP "[\fB\-\-id=@\fIname\fR or \fB\-\-id=\fIuuid\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
>
> I think, it should be [--id=(@name|uuid)]

Ack.  Done in v5.

>
> >  Creates a new record in \fItable\fR and sets the initial values of
> >  each \fIcolumn\fR.  Columns not explicitly set will receive their
> >  default values.  Outputs the UUID of the new row.
> > @@ -212,6 +212,9 @@ If \fB@\fIname\fR is specified, then the UUID for the new row may be
> >  referred to by that name elsewhere in the same \fB\*(PN\fR
> >  invocation in contexts where a UUID is expected.  Such references may
> >  precede or follow the \fBcreate\fR command.
> > +.IP
> > +If a valid \fIuuid\fR is specified, then it is used as the UUID
> > +of the new row.
> >  .
> >  .RS
> >  .IP "Caution (ovs-vsctl as example)"
> > diff --git a/lib/db-ctl-base.xml b/lib/db-ctl-base.xml
> > index f6efe98ea..4f63aa7f7 100644
> > --- a/lib/db-ctl-base.xml
> > +++ b/lib/db-ctl-base.xml
> > @@ -323,6 +323,10 @@
>
> The argument line needs an update in this file as well.

Ack.  Done in v5.

>
> >          invocation in contexts where a UUID is expected.  Such references may
> >          precede or follow the <code>create</code> command.
> >        </p>
> > +      <p>
> > +        If a valid <var>uuid</var> is specified, then it is used as the
> > +        UUID of the new row.
> > +      </p>
> >        <dl>
> >          <dt>Caution (ovs-vsctl as example)</dt>
> >          <dd>
> > diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
> > index 8797686f9..332c4075b 100644
> > --- a/lib/ovsdb-idl-provider.h
> > +++ b/lib/ovsdb-idl-provider.h
> > @@ -74,6 +74,7 @@ struct ovsdb_idl_row {
> >      struct ovs_list dst_arcs;   /* Backward arcs (ovsdb_idl_arc.dst_node). */
> >      struct ovsdb_idl_table *table; /* Containing table. */
> >      struct ovsdb_datum *old_datum; /* Committed data (null if orphaned). */
> > +    bool persist_uuid;          /* persist 'uuid' during insert txn if set. */
>
> Comments should start with a capital letter.
Ack.  Done in v5.

>
> >      bool parsed; /* Whether the row is parsed. */
> >      struct ovs_list reparse_node; /* Rows that needs to be re-parsed due to
> >                                     * insertion of a referenced row. */
> > diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
> > index 3b8741408..9ddb49273 100644
> > --- a/lib/ovsdb-idl.c
> > +++ b/lib/ovsdb-idl.c
> > @@ -2849,11 +2849,14 @@ substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
> >
> >              row = ovsdb_idl_txn_get_row(txn, &uuid);
> >              if (row && !row->old_datum && row->new_datum) {
> > -                json_destroy(json);
> > -
> > -                return json_array_create_2(
> > -                    json_string_create("named-uuid"),
> > -                    json_string_create_nocopy(ovsdb_data_row_name(&uuid)));
> > +                if (row->persist_uuid) {
> > +                    return json;
> > +                } else {
> > +                    json_destroy(json);
> > +                    return json_array_create_2(
> > +                        json_string_create("named-uuid"),
> > +                        json_string_create_nocopy(ovsdb_data_row_name(&uuid)));
> > +                }
> >              }
> >          }
> >
> > @@ -3278,9 +3281,19 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
> >
> >                  any_updates = true;
> >
> > -                json_object_put(op, "uuid-name",
> > -                                json_string_create_nocopy(
> > -                                    ovsdb_data_row_name(&row->uuid)));
> > +                char *uuid_json;
> > +                struct json *value;
> > +                if (row->persist_uuid) {
> > +                    uuid_json = "uuid";
> > +                    value = json_string_create_nocopy(
> > +                        xasprintf(UUID_FMT, UUID_ARGS(&row->uuid)));
> > +                } else {
> > +                    uuid_json = "uuid-name";
> > +                    value = json_string_create_nocopy(
> > +                                ovsdb_data_row_name(&row->uuid));
> > +                }
> > +
> > +                json_object_put(op, uuid_json, value);
> >
> >                  insert = xmalloc(sizeof *insert);
> >                  insert->dummy = row->uuid;
> > @@ -3765,6 +3778,32 @@ ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
> >      row->new_datum = NULL;
> >  }
> >
> > +static const struct ovsdb_idl_row *
> > +ovsdb_idl_txn_insert__(struct ovsdb_idl_txn *txn,
> > +                       const struct ovsdb_idl_table_class *class,
> > +                       const struct uuid *uuid,
> > +                       bool persist_uuid)
> > +{
> > +    struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
> > +
>
> Should we also ovs_assert(uuid || !persist_uuid) ?

Sounds good.  Done in v5.

Please take a  look at v5 -
https://patchwork.ozlabs.org/project/openvswitch/patch/20220718173703.296441-1-numans@ovn.org/

Numan

>
> > +    if (uuid) {
> > +        ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
> > +        row->uuid = *uuid;
> > +        row->persist_uuid = persist_uuid;
> > +    } else {
> > +        uuid_generate(&row->uuid);
> > +        row->persist_uuid = false;
> > +    }
> > +
> > +    row->table = ovsdb_idl_table_from_class(txn->idl, class);
> > +    row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
> > +    hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
> > +    hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
> > +    ovsdb_idl_add_to_indexes(row);
> > +
> > +    return row;
> > +}
> > +
> >  /* Inserts and returns a new row in the table with the specified 'class' in the
> >   * database with open transaction 'txn'.
> >   *
> > @@ -3782,22 +3821,23 @@ ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn,
> >                       const struct ovsdb_idl_table_class *class,
> >                       const struct uuid *uuid)
> >  {
> > -    struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
> > -
> > -    if (uuid) {
> > -        ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
> > -        row->uuid = *uuid;
> > -    } else {
> > -        uuid_generate(&row->uuid);
> > -    }
> > -
> > -    row->table = ovsdb_idl_table_from_class(txn->idl, class);
> > -    row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
> > -    hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
> > -    hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
> > -    ovsdb_idl_add_to_indexes(row);
> > +    return ovsdb_idl_txn_insert__(txn, class, uuid, false);
> > +}
> >
> > -    return row;
> > +/* Inserts and returns a new row in the table with the specified 'class' in the
> > + * database with open transaction 'txn'.
> > + *
> > + * The new row is assigned the specified UUID (which cannot be null).
> > + *
> > + * Usually this function is used indirectly through one of the
> > + * "insert_persist_uuid" functions generated by ovsdb-idlc. */
> > +const struct ovsdb_idl_row *
> > +ovsdb_idl_txn_insert_persist_uuid(struct ovsdb_idl_txn *txn,
> > +                                  const struct ovsdb_idl_table_class *class,
> > +                                  const struct uuid *uuid)
> > +{
> > +    ovs_assert(uuid);
> > +    return ovsdb_idl_txn_insert__(txn, class, uuid, true);
> >  }
> >
> >  static void
> > diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
> > index fbd9f671a..9a3e19f20 100644
> > --- a/lib/ovsdb-idl.h
> > +++ b/lib/ovsdb-idl.h
> > @@ -375,6 +375,9 @@ void ovsdb_idl_txn_delete(const struct ovsdb_idl_row *);
> >  const struct ovsdb_idl_row *ovsdb_idl_txn_insert(
> >      struct ovsdb_idl_txn *, const struct ovsdb_idl_table_class *,
> >      const struct uuid *);
> > +const struct ovsdb_idl_row *ovsdb_idl_txn_insert_persist_uuid(
> > +    struct ovsdb_idl_txn *txn, const struct ovsdb_idl_table_class *class,
> > +    const struct uuid *uuid);
> >
> >  struct ovsdb_idl *ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *);
> >  void ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *);
> > diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
> > index 13c535939..7e09497ea 100755
> > --- a/ovsdb/ovsdb-idlc.in
> > +++ b/ovsdb/ovsdb-idlc.in
> > @@ -362,6 +362,8 @@ struct %(s)s *%(s)s_cursor_data(struct ovsdb_idl_cursor *);
> >  void %(s)s_init(struct %(s)s *);
> >  void %(s)s_delete(const struct %(s)s *);
> >  struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
> > +struct %(s)s *%(s)s_insert_persist_uuid(
> > +    struct ovsdb_idl_txn *txn, const struct uuid *uuid);
> >
> >  /* Returns true if the tracked column referenced by 'enum %(s)s_column_id' of
> >   * the row referenced by 'struct %(s)s *' was updated since the last change
> > @@ -809,6 +811,19 @@ struct %(s)s *
> >      return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_%(tl)s, NULL));
> >  }
> >
> > +/* Inserts and returns a new row in the table "%(t)s" in the database
> > + * with open transaction 'txn'.
> > + *
> > + * The new row is assigned the UUID specified in the 'uuid' parameter
> > + * (which cannot be null).  ovsdb-server will try to assign the same
> > + * UUID when 'txn' is committed. */
> > +struct %(s)s *
> > +%(s)s_insert_persist_uuid(struct ovsdb_idl_txn *txn, const struct uuid *uuid)
> > +{
> > +    return %(s)s_cast(ovsdb_idl_txn_insert_persist_uuid(
> > +        txn, &%(p)stable_%(tl)s, uuid));
> > +}
> > +
> >  bool
> >  %(s)s_is_updated(const struct %(s)s *row, enum %(s)s_column_id column)
> >  {
> > diff --git a/python/TODO.rst b/python/TODO.rst
> > index 3a53489f1..a0b3e807a 100644
> > --- a/python/TODO.rst
> > +++ b/python/TODO.rst
> > @@ -32,3 +32,5 @@ Python Bindings To-do List
> >
> >    * Support write-only-changed monitor mode (equivalent of
> >      OVSDB_IDL_WRITE_CHANGED_ONLY).
> > +
> > +  * Support accepting the uuid for row inserts.
> > diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
> > index d6cd2c084..abf4fb9cf 100644
> > --- a/tests/ovs-vsctl.at
> > +++ b/tests/ovs-vsctl.at
> > @@ -1710,3 +1710,28 @@ ingress_policing_kpkts_rate: 100
> >  ])
> >  OVS_VSCTL_CLEANUP
> >  AT_CLEANUP
> > +
> > +AT_SETUP([ovs-vsctl create bridge with uuid])
> > +AT_KEYWORDS([create bridge with uuid])
> > +OVS_VSCTL_SETUP
> > +
> > +AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \
> > +name=tst0 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [0],[dnl
> > +c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> > +])
> > +
> > +AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \
> > +name=tst1 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [1], [ignore], [ignore])
> > +
> > +AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge], [0], [dnl
> > +c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> > +tst0
> > +])
> > +
> > +ovs-vsctl --no-wait --id=@a create bridge \
> > +name=tst1 -- add open . bridges @a
> > +
> > +AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge tst1], [0], [ignore])
> > +
> > +OVS_VSCTL_CLEANUP
> > +AT_CLEANUP
> > diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
> > index d1cfa59c5..28c032105 100644
> > --- a/tests/ovsdb-idl.at
> > +++ b/tests/ovsdb-idl.at
> > @@ -2466,3 +2466,30 @@ unix:socket2 remote has col id in table simple7
> >
> >  OVSDB_SERVER_SHUTDOWN
> >  AT_CLEANUP
> > +
> > +AT_SETUP([idl creating rows with persistent uuid - C])
> > +AT_KEYWORDS([ovsdb client idl txn])
> > +
> > +AT_CHECK([ovsdb_start_idltest "" "$abs_srcdir/idltest.ovsschema"])
> > +AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl-txn-persistent-uuid unix:socket],
> > +         [0], [stdout], [stderr])
> > +AT_CHECK([sort stdout], [0],
> > +    [[000: After inserting simple, simple3 and simple4
> > +001: table simple3: name=simple3 uset=[] uref=[c5cc12f8-eaa1-43a7-8a73-bccd18df1111] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222
> > +001: table simple4: name=simple4 uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> > +001: table simple: i=0 r=0 b=false s=simple u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333
> > +002: After inserting simple with same uuid
> > +003: table simple3: name=simple3 uset=[] uref=[c5cc12f8-eaa1-43a7-8a73-bccd18df1111] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222
> > +003: table simple4: name=simple4 uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df1111
> > +003: table simple: i=0 r=0 b=false s=simple u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333
> > +004: End test
> > +]])
> > +
> > +AT_CHECK([grep ovsdb_idl stderr | sort], [0], [dnl
> > +test-ovsdb|ovsdb_idl|transaction error: {"details":"This UUID would dnl
> > +duplicate a UUID already present within the table or deleted within dnl
> > +the same transaction.","error":"duplicate uuid","syntax":"\"c5cc12f8-eaa1-43a7-8a73-bccd18df3333\""}
> > +])
> > +
> > +OVSDB_SERVER_SHUTDOWN
> > +AT_CLEANUP
> > diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
> > index 0fef1f978..515a3015d 100644
> > --- a/tests/test-ovsdb.c
> > +++ b/tests/test-ovsdb.c
> > @@ -3389,6 +3389,63 @@ do_idl_table_column_check(struct ovs_cmdl_context *ctx)
> >      ovsdb_idl_destroy(idl);
> >  }
> >
> > +static void
> > +do_idl_txn_persistent_uuid(struct ovs_cmdl_context *ctx)
> > +{
> > +    struct ovsdb_idl *idl;
> > +    struct ovsdb_idl_txn *myTxn;
> > +    int step = 0;
> > +
> > +    idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, true, true);
> > +    ovsdb_idl_get_initial_snapshot(idl);
> > +    ovsdb_idl_run(idl);
> > +
> > +    myTxn = ovsdb_idl_txn_create(idl);
> > +
> > +    struct uuid uuid1;
> > +    uuid_from_string(&uuid1, "c5cc12f8-eaa1-43a7-8a73-bccd18df1111");
> > +
> > +    struct uuid uuid2;
> > +    uuid_from_string(&uuid2, "c5cc12f8-eaa1-43a7-8a73-bccd18df2222");
> > +
> > +    struct idltest_simple4 *simple4_row =
> > +        idltest_simple4_insert_persist_uuid(myTxn, &uuid1);
> > +    idltest_simple4_set_name(simple4_row, "simple4");
> > +
> > +    struct idltest_simple3 *simple3_row =
> > +        idltest_simple3_insert_persist_uuid(myTxn, &uuid2);
> > +    idltest_simple3_set_name(simple3_row, "simple3");
> > +    idltest_simple3_set_uref(simple3_row, &simple4_row, 1);
> > +
> > +    struct uuid uuid3;
> > +    uuid_from_string(&uuid3, "c5cc12f8-eaa1-43a7-8a73-bccd18df3333");
> > +
> > +    struct idltest_simple *simple_row =
> > +        idltest_simple_insert_persist_uuid(myTxn, &uuid3);
> > +    idltest_simple_set_s(simple_row, "simple");
> > +
> > +    ovsdb_idl_txn_commit_block(myTxn);
> > +    ovsdb_idl_txn_destroy(myTxn);
> > +    ovsdb_idl_get_initial_snapshot(idl);
> > +    printf("%03d: After inserting simple, simple3 and simple4\n", step++);
> > +    print_idl(idl, step++, false);
> > +
> > +    /* Create another txn, insert the row in simple table with the existing
> > +     * uuid. */
> > +    myTxn = ovsdb_idl_txn_create(idl);
> > +    simple_row =
> > +        idltest_simple_insert_persist_uuid(myTxn, &uuid3);
> > +    idltest_simple_set_s(simple_row, "simple_foo");
> > +    ovsdb_idl_txn_commit_block(myTxn);
> > +    ovsdb_idl_txn_destroy(myTxn);
> > +    ovsdb_idl_get_initial_snapshot(idl);
> > +    printf("%03d: After inserting simple with same uuid\n", step++);
> > +    print_idl(idl, step++, false);
> > +
> > +    ovsdb_idl_destroy(idl);
> > +    printf("%03d: End test\n", step++);
> > +}
> > +
> >  static struct ovs_cmdl_command all_commands[] = {
> >      { "log-io", NULL, 2, INT_MAX, do_log_io, OVS_RO },
> >      { "default-atoms", NULL, 0, 0, do_default_atoms, OVS_RO },
> > @@ -3429,6 +3486,8 @@ static struct ovs_cmdl_command all_commands[] = {
> >          do_idl_partial_update_set_column, OVS_RO },
> >      { "idl-table-column-check", NULL, 2, 2,
> >          do_idl_table_column_check, OVS_RO },
> > +    { "idl-txn-persistent-uuid", NULL, 1, INT_MAX,
> > +        do_idl_txn_persistent_uuid, OVS_RO },
> >      { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
> >      { NULL, NULL, 0, 0, NULL, OVS_RO },
> >  };
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
index 707456158..f39e090a0 100644
--- a/lib/db-ctl-base.c
+++ b/lib/db-ctl-base.c
@@ -1732,28 +1732,42 @@  cmd_create(struct ctl_context *ctx)
     const struct ovsdb_idl_row *row;
     const struct uuid *uuid = NULL;
     int i;
+    struct uuid uuid_;
+    bool persist_uuid = false;
 
     ctx->error = get_table(table_name, &table);
     if (ctx->error) {
         return;
     }
+
     if (id) {
-        struct ovsdb_symbol *symbol = NULL;
+        if (uuid_from_string(&uuid_, id)) {
+            uuid = &uuid_;
+            persist_uuid = true;
+        } else {
+            struct ovsdb_symbol *symbol = NULL;
 
-        ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
-        if (ctx->error) {
-            return;
-        }
-        if (table->is_root) {
-            /* This table is in the root set, meaning that rows created in it
-             * won't disappear even if they are unreferenced, so disable
-             * warnings about that by pretending that there is a reference. */
-            symbol->strong_ref = true;
+            ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
+            if (ctx->error) {
+                return;
+            }
+            if (table->is_root) {
+                /* This table is in the root set, meaning that rows created in
+                * it won't disappear even if they are unreferenced, so disable
+                * warnings about that by pretending that there is a
+                * reference. */
+                symbol->strong_ref = true;
+            }
+            uuid = &symbol->uuid;
         }
-        uuid = &symbol->uuid;
     }
 
-    row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
+    if (persist_uuid) {
+        row = ovsdb_idl_txn_insert_persist_uuid(ctx->txn, table, uuid);
+    } else {
+        row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
+    }
+
     for (i = 2; i < ctx->argc; i++) {
         ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
         if (ctx->error) {
diff --git a/lib/db-ctl-base.man b/lib/db-ctl-base.man
index 9c786f298..4dc165f94 100644
--- a/lib/db-ctl-base.man
+++ b/lib/db-ctl-base.man
@@ -203,7 +203,7 @@  Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not
 exist.  With \fB\-\-if-exists\fR, this command does nothing if
 \fIrecord\fR does not exist.
 .
-.IP "[\fB\-\-id=@\fIname\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
+.IP "[\fB\-\-id=@\fIname\fR or \fB\-\-id=\fIuuid\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
 Creates a new record in \fItable\fR and sets the initial values of
 each \fIcolumn\fR.  Columns not explicitly set will receive their
 default values.  Outputs the UUID of the new row.
@@ -212,6 +212,9 @@  If \fB@\fIname\fR is specified, then the UUID for the new row may be
 referred to by that name elsewhere in the same \fB\*(PN\fR
 invocation in contexts where a UUID is expected.  Such references may
 precede or follow the \fBcreate\fR command.
+.IP
+If a valid \fIuuid\fR is specified, then it is used as the UUID
+of the new row.
 .
 .RS
 .IP "Caution (ovs-vsctl as example)"
diff --git a/lib/db-ctl-base.xml b/lib/db-ctl-base.xml
index f6efe98ea..4f63aa7f7 100644
--- a/lib/db-ctl-base.xml
+++ b/lib/db-ctl-base.xml
@@ -323,6 +323,10 @@ 
         invocation in contexts where a UUID is expected.  Such references may
         precede or follow the <code>create</code> command.
       </p>
+      <p>
+        If a valid <var>uuid</var> is specified, then it is used as the
+        UUID of the new row.
+      </p>
       <dl>
         <dt>Caution (ovs-vsctl as example)</dt>
         <dd>
diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
index 8797686f9..332c4075b 100644
--- a/lib/ovsdb-idl-provider.h
+++ b/lib/ovsdb-idl-provider.h
@@ -74,6 +74,7 @@  struct ovsdb_idl_row {
     struct ovs_list dst_arcs;   /* Backward arcs (ovsdb_idl_arc.dst_node). */
     struct ovsdb_idl_table *table; /* Containing table. */
     struct ovsdb_datum *old_datum; /* Committed data (null if orphaned). */
+    bool persist_uuid;          /* persist 'uuid' during insert txn if set. */
     bool parsed; /* Whether the row is parsed. */
     struct ovs_list reparse_node; /* Rows that needs to be re-parsed due to
                                    * insertion of a referenced row. */
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index 3b8741408..9ddb49273 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -2849,11 +2849,14 @@  substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
 
             row = ovsdb_idl_txn_get_row(txn, &uuid);
             if (row && !row->old_datum && row->new_datum) {
-                json_destroy(json);
-
-                return json_array_create_2(
-                    json_string_create("named-uuid"),
-                    json_string_create_nocopy(ovsdb_data_row_name(&uuid)));
+                if (row->persist_uuid) {
+                    return json;
+                } else {
+                    json_destroy(json);
+                    return json_array_create_2(
+                        json_string_create("named-uuid"),
+                        json_string_create_nocopy(ovsdb_data_row_name(&uuid)));
+                }
             }
         }
 
@@ -3278,9 +3281,19 @@  ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
 
                 any_updates = true;
 
-                json_object_put(op, "uuid-name",
-                                json_string_create_nocopy(
-                                    ovsdb_data_row_name(&row->uuid)));
+                char *uuid_json;
+                struct json *value;
+                if (row->persist_uuid) {
+                    uuid_json = "uuid";
+                    value = json_string_create_nocopy(
+                        xasprintf(UUID_FMT, UUID_ARGS(&row->uuid)));
+                } else {
+                    uuid_json = "uuid-name";
+                    value = json_string_create_nocopy(
+                                ovsdb_data_row_name(&row->uuid));
+                }
+
+                json_object_put(op, uuid_json, value);
 
                 insert = xmalloc(sizeof *insert);
                 insert->dummy = row->uuid;
@@ -3765,6 +3778,32 @@  ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
     row->new_datum = NULL;
 }
 
+static const struct ovsdb_idl_row *
+ovsdb_idl_txn_insert__(struct ovsdb_idl_txn *txn,
+                       const struct ovsdb_idl_table_class *class,
+                       const struct uuid *uuid,
+                       bool persist_uuid)
+{
+    struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
+
+    if (uuid) {
+        ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
+        row->uuid = *uuid;
+        row->persist_uuid = persist_uuid;
+    } else {
+        uuid_generate(&row->uuid);
+        row->persist_uuid = false;
+    }
+
+    row->table = ovsdb_idl_table_from_class(txn->idl, class);
+    row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
+    hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
+    hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
+    ovsdb_idl_add_to_indexes(row);
+
+    return row;
+}
+
 /* Inserts and returns a new row in the table with the specified 'class' in the
  * database with open transaction 'txn'.
  *
@@ -3782,22 +3821,23 @@  ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn,
                      const struct ovsdb_idl_table_class *class,
                      const struct uuid *uuid)
 {
-    struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
-
-    if (uuid) {
-        ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
-        row->uuid = *uuid;
-    } else {
-        uuid_generate(&row->uuid);
-    }
-
-    row->table = ovsdb_idl_table_from_class(txn->idl, class);
-    row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
-    hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
-    hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
-    ovsdb_idl_add_to_indexes(row);
+    return ovsdb_idl_txn_insert__(txn, class, uuid, false);
+}
 
-    return row;
+/* Inserts and returns a new row in the table with the specified 'class' in the
+ * database with open transaction 'txn'.
+ *
+ * The new row is assigned the specified UUID (which cannot be null).
+ *
+ * Usually this function is used indirectly through one of the
+ * "insert_persist_uuid" functions generated by ovsdb-idlc. */
+const struct ovsdb_idl_row *
+ovsdb_idl_txn_insert_persist_uuid(struct ovsdb_idl_txn *txn,
+                                  const struct ovsdb_idl_table_class *class,
+                                  const struct uuid *uuid)
+{
+    ovs_assert(uuid);
+    return ovsdb_idl_txn_insert__(txn, class, uuid, true);
 }
 
 static void
diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
index fbd9f671a..9a3e19f20 100644
--- a/lib/ovsdb-idl.h
+++ b/lib/ovsdb-idl.h
@@ -375,6 +375,9 @@  void ovsdb_idl_txn_delete(const struct ovsdb_idl_row *);
 const struct ovsdb_idl_row *ovsdb_idl_txn_insert(
     struct ovsdb_idl_txn *, const struct ovsdb_idl_table_class *,
     const struct uuid *);
+const struct ovsdb_idl_row *ovsdb_idl_txn_insert_persist_uuid(
+    struct ovsdb_idl_txn *txn, const struct ovsdb_idl_table_class *class,
+    const struct uuid *uuid);
 
 struct ovsdb_idl *ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *);
 void ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *);
diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
index 13c535939..7e09497ea 100755
--- a/ovsdb/ovsdb-idlc.in
+++ b/ovsdb/ovsdb-idlc.in
@@ -362,6 +362,8 @@  struct %(s)s *%(s)s_cursor_data(struct ovsdb_idl_cursor *);
 void %(s)s_init(struct %(s)s *);
 void %(s)s_delete(const struct %(s)s *);
 struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
+struct %(s)s *%(s)s_insert_persist_uuid(
+    struct ovsdb_idl_txn *txn, const struct uuid *uuid);
 
 /* Returns true if the tracked column referenced by 'enum %(s)s_column_id' of
  * the row referenced by 'struct %(s)s *' was updated since the last change
@@ -809,6 +811,19 @@  struct %(s)s *
     return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_%(tl)s, NULL));
 }
 
+/* Inserts and returns a new row in the table "%(t)s" in the database
+ * with open transaction 'txn'.
+ *
+ * The new row is assigned the UUID specified in the 'uuid' parameter
+ * (which cannot be null).  ovsdb-server will try to assign the same
+ * UUID when 'txn' is committed. */
+struct %(s)s *
+%(s)s_insert_persist_uuid(struct ovsdb_idl_txn *txn, const struct uuid *uuid)
+{
+    return %(s)s_cast(ovsdb_idl_txn_insert_persist_uuid(
+        txn, &%(p)stable_%(tl)s, uuid));
+}
+
 bool
 %(s)s_is_updated(const struct %(s)s *row, enum %(s)s_column_id column)
 {
diff --git a/python/TODO.rst b/python/TODO.rst
index 3a53489f1..a0b3e807a 100644
--- a/python/TODO.rst
+++ b/python/TODO.rst
@@ -32,3 +32,5 @@  Python Bindings To-do List
 
   * Support write-only-changed monitor mode (equivalent of
     OVSDB_IDL_WRITE_CHANGED_ONLY).
+
+  * Support accepting the uuid for row inserts.
diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
index d6cd2c084..abf4fb9cf 100644
--- a/tests/ovs-vsctl.at
+++ b/tests/ovs-vsctl.at
@@ -1710,3 +1710,28 @@  ingress_policing_kpkts_rate: 100
 ])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
+
+AT_SETUP([ovs-vsctl create bridge with uuid])
+AT_KEYWORDS([create bridge with uuid])
+OVS_VSCTL_SETUP
+
+AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \
+name=tst0 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [0],[dnl
+c5cc12f8-eaa1-43a7-8a73-bccd18df1111
+])
+
+AT_CHECK([ovs-vsctl --no-wait --id=c5cc12f8-eaa1-43a7-8a73-bccd18df1111 create bridge \
+name=tst1 -- add open . bridges c5cc12f8-eaa1-43a7-8a73-bccd18df1111], [1], [ignore], [ignore])
+
+AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge], [0], [dnl
+c5cc12f8-eaa1-43a7-8a73-bccd18df1111
+tst0
+])
+
+ovs-vsctl --no-wait --id=@a create bridge \
+name=tst1 -- add open . bridges @a
+
+AT_CHECK([ovs-vsctl --no-wait --bare --columns _uuid,name list bridge tst1], [0], [ignore])
+
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
index d1cfa59c5..28c032105 100644
--- a/tests/ovsdb-idl.at
+++ b/tests/ovsdb-idl.at
@@ -2466,3 +2466,30 @@  unix:socket2 remote has col id in table simple7
 
 OVSDB_SERVER_SHUTDOWN
 AT_CLEANUP
+
+AT_SETUP([idl creating rows with persistent uuid - C])
+AT_KEYWORDS([ovsdb client idl txn])
+
+AT_CHECK([ovsdb_start_idltest "" "$abs_srcdir/idltest.ovsschema"])
+AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl-txn-persistent-uuid unix:socket],
+         [0], [stdout], [stderr])
+AT_CHECK([sort stdout], [0],
+    [[000: After inserting simple, simple3 and simple4
+001: table simple3: name=simple3 uset=[] uref=[c5cc12f8-eaa1-43a7-8a73-bccd18df1111] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222
+001: table simple4: name=simple4 uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df1111
+001: table simple: i=0 r=0 b=false s=simple u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333
+002: After inserting simple with same uuid
+003: table simple3: name=simple3 uset=[] uref=[c5cc12f8-eaa1-43a7-8a73-bccd18df1111] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df2222
+003: table simple4: name=simple4 uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df1111
+003: table simple: i=0 r=0 b=false s=simple u=00000000-0000-0000-0000-000000000000 ia=[] ra=[] ba=[] sa=[] ua=[] uuid=c5cc12f8-eaa1-43a7-8a73-bccd18df3333
+004: End test
+]])
+
+AT_CHECK([grep ovsdb_idl stderr | sort], [0], [dnl
+test-ovsdb|ovsdb_idl|transaction error: {"details":"This UUID would dnl
+duplicate a UUID already present within the table or deleted within dnl
+the same transaction.","error":"duplicate uuid","syntax":"\"c5cc12f8-eaa1-43a7-8a73-bccd18df3333\""}
+])
+
+OVSDB_SERVER_SHUTDOWN
+AT_CLEANUP
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
index 0fef1f978..515a3015d 100644
--- a/tests/test-ovsdb.c
+++ b/tests/test-ovsdb.c
@@ -3389,6 +3389,63 @@  do_idl_table_column_check(struct ovs_cmdl_context *ctx)
     ovsdb_idl_destroy(idl);
 }
 
+static void
+do_idl_txn_persistent_uuid(struct ovs_cmdl_context *ctx)
+{
+    struct ovsdb_idl *idl;
+    struct ovsdb_idl_txn *myTxn;
+    int step = 0;
+
+    idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, true, true);
+    ovsdb_idl_get_initial_snapshot(idl);
+    ovsdb_idl_run(idl);
+
+    myTxn = ovsdb_idl_txn_create(idl);
+
+    struct uuid uuid1;
+    uuid_from_string(&uuid1, "c5cc12f8-eaa1-43a7-8a73-bccd18df1111");
+
+    struct uuid uuid2;
+    uuid_from_string(&uuid2, "c5cc12f8-eaa1-43a7-8a73-bccd18df2222");
+
+    struct idltest_simple4 *simple4_row =
+        idltest_simple4_insert_persist_uuid(myTxn, &uuid1);
+    idltest_simple4_set_name(simple4_row, "simple4");
+
+    struct idltest_simple3 *simple3_row =
+        idltest_simple3_insert_persist_uuid(myTxn, &uuid2);
+    idltest_simple3_set_name(simple3_row, "simple3");
+    idltest_simple3_set_uref(simple3_row, &simple4_row, 1);
+
+    struct uuid uuid3;
+    uuid_from_string(&uuid3, "c5cc12f8-eaa1-43a7-8a73-bccd18df3333");
+
+    struct idltest_simple *simple_row =
+        idltest_simple_insert_persist_uuid(myTxn, &uuid3);
+    idltest_simple_set_s(simple_row, "simple");
+
+    ovsdb_idl_txn_commit_block(myTxn);
+    ovsdb_idl_txn_destroy(myTxn);
+    ovsdb_idl_get_initial_snapshot(idl);
+    printf("%03d: After inserting simple, simple3 and simple4\n", step++);
+    print_idl(idl, step++, false);
+
+    /* Create another txn, insert the row in simple table with the existing
+     * uuid. */
+    myTxn = ovsdb_idl_txn_create(idl);
+    simple_row =
+        idltest_simple_insert_persist_uuid(myTxn, &uuid3);
+    idltest_simple_set_s(simple_row, "simple_foo");
+    ovsdb_idl_txn_commit_block(myTxn);
+    ovsdb_idl_txn_destroy(myTxn);
+    ovsdb_idl_get_initial_snapshot(idl);
+    printf("%03d: After inserting simple with same uuid\n", step++);
+    print_idl(idl, step++, false);
+
+    ovsdb_idl_destroy(idl);
+    printf("%03d: End test\n", step++);
+}
+
 static struct ovs_cmdl_command all_commands[] = {
     { "log-io", NULL, 2, INT_MAX, do_log_io, OVS_RO },
     { "default-atoms", NULL, 0, 0, do_default_atoms, OVS_RO },
@@ -3429,6 +3486,8 @@  static struct ovs_cmdl_command all_commands[] = {
         do_idl_partial_update_set_column, OVS_RO },
     { "idl-table-column-check", NULL, 2, 2,
         do_idl_table_column_check, OVS_RO },
+    { "idl-txn-persistent-uuid", NULL, 1, INT_MAX,
+        do_idl_txn_persistent_uuid, OVS_RO },
     { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
     { NULL, NULL, 0, 0, NULL, OVS_RO },
 };