'

On Wed, Aug 25, 2021 at 6:06 PM Ilya Maximets <i.maxim...@ovn.org> wrote:
>
> On 8/25/21 5:12 AM, num...@ovn.org wrote:
> > From: Numan Siddique <nusid...@redhat.com>
> >
> > This patch adds 2 new APIs in the ovsdb-idl client library
> >  - ovsdb_idl_has_table() and ovsdb_idl_has_column_in_table() to
> > query if a table and a column is present in the IDL or not.  This
> > is required for scenarios where the server schema is old and
> > missing a table or column and the client (built with a new schema
> > version) does a transaction with the missing table or column.  This
> > results in a continuous loop of transaction failures.
> >
> > OVN would require the API - ovsdb_idl_has_table() to address this issue
> > when an old ovsdb-server is used (OVS 2.11) which has the 'datapath'
> > table missing.  A recent commit in OVN creates a 'datapath' row
> > in the local ovsdb-server.  ovsdb_idl_has_column_in_table() would be
> > useful to have.
> >
> > Related issue: https://bugzilla.redhat.com/show_bug.cgi?id=1992705
> > Signed-off-by: Numan Siddique <nusid...@redhat.com>
> > ---
> >  lib/ovsdb-idl-provider.h |  4 +++
> >  lib/ovsdb-idl.c          | 36 ++++++++++++++++++++
> >  lib/ovsdb-idl.h          |  3 ++
> >  tests/ovsdb-idl.at       | 38 +++++++++++++++++++++
> >  tests/test-ovsdb.c       | 73 ++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 154 insertions(+)
> >
> > diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
> > index 0f38f9b34..0f122e23c 100644
> > --- a/lib/ovsdb-idl-provider.h
> > +++ b/lib/ovsdb-idl-provider.h
> > @@ -24,6 +24,7 @@
> >  #include "ovsdb-set-op.h"
> >  #include "ovsdb-types.h"
> >  #include "openvswitch/shash.h"
> > +#include "sset.h"
> >  #include "uuid.h"
> >
> >  #ifdef __cplusplus
> > @@ -117,9 +118,12 @@ struct ovsdb_idl_table {
> >      bool need_table;         /* Monitor table even if no columns are 
> > selected
> >                                * for replication. */
> >      struct shash columns;    /* Contains "const struct ovsdb_idl_column 
> > *"s. */
> > +    struct sset schema_columns; /* Column names from schema. */
>
> Why did you decide to go with sset instead of 'in_server_schema'
> boolean for the struct ovsdb_idl_column?

Two reasons:
1.  The ovsdb_idl_column instances for each table columns are
generated automatically by ovsdb/ovsdb-idlc.in
2.  'struct ovsdb_idl_column *columns' is const in 'struct
ovsdb_idl_table_class'
     https://github.com/openvswitch/ovs/blob/master/lib/ovsdb-idl.c#L722


>
> >      struct hmap rows;        /* Contains "struct ovsdb_idl_row"s. */
> >      struct ovsdb_idl *idl;   /* Containing IDL instance. */
> >      unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX];
> > +    bool in_server_schema;   /* Indicates if this table is in the server 
> > schema
> > +                              * or not. */
> >      struct ovs_list indexes;    /* Contains "struct ovsdb_idl_index"s */
> >      struct ovs_list track_list; /* Tracked rows 
> > (ovsdb_idl_row.track_node). */
> >  };
> > diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
> > index 2198c69c6..b2dfff46c 100644
> > --- a/lib/ovsdb-idl.c
> > +++ b/lib/ovsdb-idl.c
> > @@ -287,6 +287,8 @@ ovsdb_idl_create_unconnected(const struct 
> > ovsdb_idl_class *class,
> >              = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]
> >              = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
> >          table->idl = idl;
> > +        table->in_server_schema = true;  /* Assume it's in server schema. 
> > */
>
> Hmm.  Why is that?  We do not make assumptions about columns,
> we should, probably, not do that for tables too.  What do
> you think?

I was thinking of the scenario where user calls ovsdb_has_table() at
the very beginning.
I'll remove it in v5.


>
> > +        sset_init(&table->schema_columns);
> >      }
> >
> >      return idl;
> > @@ -337,6 +339,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl)
> >              struct ovsdb_idl_table *table = &idl->tables[i];
> >              ovsdb_idl_destroy_indexes(table);
> >              shash_destroy(&table->columns);
> > +            sset_destroy(&table->schema_columns);
> >              hmap_destroy(&table->rows);
> >              free(table->modes);
> >          }
> > @@ -718,6 +721,7 @@ ovsdb_idl_compose_monitor_request(const struct json 
> > *schema_json, void *idl_)
> >
> >          struct json *columns
> >              = table->need_table ? json_array_create_empty() : NULL;
> > +        sset_clear(&table->schema_columns);
> >          for (size_t j = 0; j < tc->n_columns; j++) {
> >              const struct ovsdb_idl_column *column = &tc->columns[j];
> >              bool idl_has_column = (table_schema &&
> > @@ -741,6 +745,7 @@ ovsdb_idl_compose_monitor_request(const struct json 
> > *schema_json, void *idl_)
> >                  }
> >                  json_array_add(columns, json_string_create(column->name));
> >              }
> > +            sset_add(&table->schema_columns, column->name);
> >          }
> >
> >          if (columns) {
> > @@ -749,7 +754,12 @@ ovsdb_idl_compose_monitor_request(const struct json 
> > *schema_json, void *idl_)
> >                            "(database needs upgrade?)",
> >                            idl->class_->database, table->class_->name);
> >                  json_destroy(columns);
> > +                /* Set 'table->in_server_schema' to false so that this can 
> > be
> > +                 * excluded from transactions. */
>
> This comment seems redundant, since the functionality is in
> a different patch.

Ack.

>
> > +                table->in_server_schema = false;
> >                  continue;
> > +            } else if (schema && table_schema) {
> > +                table->in_server_schema = true;
> >              }
> >
> >              monitor_request = json_object_create();
> > @@ -4256,3 +4266,29 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop 
> > *loop)
> >
> >      return retval;
> >  }
> > +
> > +static struct ovsdb_idl_table*
> > +ovsdb_idl_get_table(struct ovsdb_idl *idl, const char *table_name)
> > +{
> > +    struct ovsdb_idl_table *table = shash_find_data(&idl->table_by_name,
> > +                                                    table_name);
> > +    return table && table->in_server_schema ? table : NULL;
>
> It's better to parenthesize the condition, the priority of
> operations is not obvious.

Ack.

>
> > +}
> > +
> > +bool
> > +ovsdb_idl_has_table(struct ovsdb_idl *idl, const char *table_name)
> > +{
> > +    return ovsdb_idl_get_table(idl, table_name) ? true: false;
>
> Missing space before the ':'.

Ack.


>
> > +}
> > +
> > +bool
> > +ovsdb_idl_has_column_in_table(struct ovsdb_idl *idl, const char 
> > *table_name,
> > +                              const char *column_name)
> > +{
> > +    struct ovsdb_idl_table *table = ovsdb_idl_get_table(idl, table_name);
>
> Empty line here would be nice to have.

Ack.


>
> > +    if (table && sset_find(&table->schema_columns, column_name)) {
> > +        return true;
> > +    }
> > +
> > +    return false;
> > +}
> > diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
> > index d93483245..48425b39a 100644
> > --- a/lib/ovsdb-idl.h
> > +++ b/lib/ovsdb-idl.h
> > @@ -474,6 +474,9 @@ void ovsdb_idl_cursor_next_eq(struct ovsdb_idl_cursor 
> > *);
> >
> >  struct ovsdb_idl_row *ovsdb_idl_cursor_data(struct ovsdb_idl_cursor *);
> >
> > +bool ovsdb_idl_has_table(struct ovsdb_idl *, const char *table_name);
> > +bool ovsdb_idl_has_column_in_table(struct ovsdb_idl *, const char 
> > *table_name,
> > +                                   const char *column_name);
>
> This is a section of a header related to indexes and iteration.
> These functions should be defined somewhere close to
> ovsdb_idl_check_consistency() or ovsdb_idl_get_class().

Ack.


>
> >  #ifdef __cplusplus
> >  }
> >  #endif
> > diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
> > index 1386f1377..b11fabe70 100644
> > --- a/tests/ovsdb-idl.at
> > +++ b/tests/ovsdb-idl.at
> > @@ -2372,3 +2372,41 @@ OVSDB_CHECK_CLUSTER_IDL([simple idl, initially 
> > empty, force reconnect],
> >  [],
> >  [],
> >  reconnect.*waiting .* seconds before reconnect)
> > +
> > +AT_SETUP([idl table and column presence check])
> > +AT_KEYWORDS([ovsdb server idl table column check])
> > +AT_CHECK([ovsdb_start_idltest "" "$abs_srcdir/idltest2.ovsschema"])
> > +
> > +ovsdb-tool create db2 $abs_srcdir/idltest.ovsschema
> > +ovsdb-server -vconsole:warn --log-file=ovsdb-server2.log --detach 
> > --no-chdir --pidfile=ovsdb-server2.pid --remote=punix:socket2 db2
>
> Above two commands should be executed with AT_CHECK.
> It also would be nice to wrap the long line with 'dnl' inside AT_CHECK.

Ack.

>
> > +on_exit 'kill `cat ovsdb-server2.pid`'
> > +
> > +# In this test, test-ovsdb first connects to the server with schema
> > +# idltest2.ovsschema and outputs the presence of tables and columns.
> > +# And then it connectes to the server with the schema idltest.ovsschema
> > +# and does the same.
> > +AT_CHECK([test-ovsdb -vconsole:off -t10 idl-table-column-check unix:socket 
> > unix:socket2 \
>
> It's better to use 'dnl' for breaking lines, so it can be properly
> printed in the testsuite log.

Ack.

>
> > +simple link1 link2 simple5 foo simple5:irefmap simple5:foo link1:l2],
> > +[0], [dnl
> > +table simple is present
> > +table link1 is present
> > +table link2 is not present
> > +table simple5 is not present
> > +table foo is not present
> > +table simple5 is not present
> > +table simple5 is not present
> > +column l2 in table link1 is not present
> > +--- remote 1 done ---
> > +table simple is present
> > +table link1 is present
> > +table link2 is present
> > +table simple5 is present
> > +table foo is not present
> > +column irefmap in table simple5 is present
> > +column foo in table simple5 is not present
> > +column l2 in table link1 is present
> > +--- remote 2 done ---
> > +])
> > +
> > +OVSDB_SERVER_SHUTDOWN
> > +AT_CLEANUP
> > diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
> > index daa55dab7..462d2e57e 100644
> > --- a/tests/test-ovsdb.c
> > +++ b/tests/test-ovsdb.c
> > @@ -3268,6 +3268,77 @@ do_idl_compound_index(struct ovs_cmdl_context *ctx)
> >      printf("%03d: done\n", step);
> >  }
> >
> > +static void
> > +do_idl_table_column_check(struct ovs_cmdl_context *ctx)
> > +{
> > +    struct jsonrpc *rpc;
> > +    struct ovsdb_idl *idl;
> > +    unsigned int seqno = 0;
> > +    int error;
> > +    int i;
> > +
> > +    if (ctx->argc < 3) {
> > +        exit(1);
> > +    }
> > +
> > +    idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, true, true);
> > +    ovsdb_idl_set_leader_only(idl, false);
> > +    struct stream *stream;
> > +
> > +    error = stream_open_block(jsonrpc_stream_open(ctx->argv[1], &stream,
> > +                              DSCP_DEFAULT), -1, &stream);
> > +    if (error) {
> > +        ovs_fatal(error, "failed to connect to \"%s\"", ctx->argv[1]);
> > +    }
> > +    rpc = jsonrpc_open(stream);
> > +
> > +    for (int r = 1; r <= 2; r++) {
> > +        ovsdb_idl_set_remote(idl, ctx->argv[r], true);
> > +        ovsdb_idl_force_reconnect(idl);
> > +
> > +        /* Wait for update. */
> > +        for (;;) {
> > +            ovsdb_idl_run(idl);
> > +            ovsdb_idl_check_consistency(idl);
> > +            if (ovsdb_idl_get_seqno(idl) != seqno) {
> > +                break;
> > +            }
> > +            jsonrpc_run(rpc);
> > +
> > +            ovsdb_idl_wait(idl);
> > +            jsonrpc_wait(rpc);
> > +            poll_block();
> > +        }
> > +
> > +        seqno = ovsdb_idl_get_seqno(idl);
> > +
> > +        for (i = 3; i < ctx->argc; i++) {
> > +            char *save_ptr2 = NULL;
> > +            char *arg  = xstrdup(ctx->argv[i]);
> > +            char *table_name = strtok_r(arg, ":", &save_ptr2);
> > +            char *column_name = strtok_r(NULL, ":", &save_ptr2);
> > +
> > +            bool table_present = ovsdb_idl_has_table(idl, table_name);
> > +            if (!table_present || !column_name) {
> > +                printf("table %s %s present\n", table_name,
> > +                    table_present ? "is" : "is not");
>
> Please shif this line to be on the same level with the text inside
> the '()' above.

Ack.

>
> > +            }
> > +            if (table_present && column_name) {
> > +                printf("column %s in table %s %s present\n", column_name,
> > +                    table_name,
> > +                    ovsdb_idl_has_column_in_table(idl, table_name,
> > +                                                    column_name) ?
> > +                    "is" : "is not");
>
> Maybe:
>
>                 bool in = ovsdb_idl_has_column_in_table(idl, table_name,
>                                                         column_name);
>                 printf("column %s in table %s %s present\n",
>                        column_name, table_name, in ? "is" : "is not");
> ?
>

Ack.


> > +            }
> > +            free(arg);
> > +        }
> > +        printf("--- remote %d done ---\n", r);
> > +    }
> > +
> > +    jsonrpc_close(rpc);
> > +    ovsdb_idl_destroy(idl);
> > +}
> > +
> >  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 },
> > @@ -3306,6 +3377,8 @@ static struct ovs_cmdl_command all_commands[] = {
> >          do_idl_partial_update_map_column, OVS_RO },
> >      { "idl-partial-update-set-column", NULL, 1, INT_MAX,
> >          do_idl_partial_update_set_column, OVS_RO },
> > +    { "idl-table-column-check", NULL, 1, INT_MAX,
>
> It should be 2, I think, instead of 1, right?  And you may remove
> the check for number of arguments from the function.

Correct.  I'll fix in v5.

Thanks for the review.

Numan

>
> > +        do_idl_table_column_check, OVS_RO },
> >      { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
> >      { NULL, NULL, 0, 0, NULL, OVS_RO },
> >  };
> >
>
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to