From ce5a3e38dacd8042dd7c4de7be24aca9c2887103 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 2 Jun 2010 11:08:03 -0700 Subject: [PATCH] ovs-vsctl: Support references among records at creation time. This makes it easy to create a bunch of records that are all related to each other in a single ovs-vsctl invocation. It adds an example to the ovs-vsctl manpage. --- lib/ovsdb-data.c | 53 +++++++++++++++++++------- lib/ovsdb-data.h | 7 +++- lib/ovsdb-idl.c | 12 +++++- lib/ovsdb-idl.h | 3 +- lib/shash.c | 12 +++++- lib/shash.h | 1 + ovsdb/ovsdb-idlc.in | 2 +- tests/test-ovsdb.c | 4 +- utilities/ovs-vsctl.8.in | 20 ++++++++-- utilities/ovs-vsctl.c | 81 +++++++++++++++++++++++++++++++--------- 10 files changed, 151 insertions(+), 44 deletions(-) diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c index 42fdf1ea..ee98c80d 100644 --- a/lib/ovsdb-data.c +++ b/lib/ovsdb-data.c @@ -366,7 +366,7 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) static char * ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, - const char *s) + const char *s, struct ovsdb_symbol_table *symtab) { switch (type) { case OVSDB_TYPE_VOID: @@ -427,7 +427,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, break; case OVSDB_TYPE_UUID: - if (!uuid_from_string(&atom->uuid, s)) { + if (*s == '@') { + atom->uuid = ovsdb_symbol_table_insert(symtab, s)->uuid; + } else if (!uuid_from_string(&atom->uuid, s)) { return xasprintf("\"%s\" is not a valid UUID", s); } break; @@ -454,19 +456,24 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type, * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise * an arbitrary string. * - * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format. + * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format. If 'symtab' is nonnull, + * then an identifier beginning with '@' is also acceptable. If the + * named identifier is already in 'symtab', then the associated UUID is + * used; otherwise, a new, random UUID is used and added to the symbol + * table. * * Returns a null pointer if successful, otherwise an error message describing * the problem. The caller is responsible for freeing the error. */ char * ovsdb_atom_from_string(union ovsdb_atom *atom, - const struct ovsdb_base_type *base, const char *s) + const struct ovsdb_base_type *base, const char *s, + struct ovsdb_symbol_table *symtab) { struct ovsdb_error *error; char *msg; - msg = ovsdb_atom_from_string__(atom, base->type, s); + msg = ovsdb_atom_from_string__(atom, base->type, s, symtab); if (msg) { return msg; } @@ -1028,13 +1035,13 @@ skip_spaces(const char *p) static char * parse_atom_token(const char **s, const struct ovsdb_base_type *base, - union ovsdb_atom *atom) + union ovsdb_atom *atom, struct ovsdb_symbol_table *symtab) { char *token, *error; error = ovsdb_token_parse(s, &token); if (!error) { - error = ovsdb_atom_from_string(atom, base, token); + error = ovsdb_atom_from_string(atom, base, token, symtab); free(token); } return error; @@ -1042,18 +1049,19 @@ parse_atom_token(const char **s, const struct ovsdb_base_type *base, static char * parse_key_value(const char **s, const struct ovsdb_type *type, - union ovsdb_atom *key, union ovsdb_atom *value) + union ovsdb_atom *key, union ovsdb_atom *value, + struct ovsdb_symbol_table *symtab) { const char *start = *s; char *error; - error = parse_atom_token(s, &type->key, key); + error = parse_atom_token(s, &type->key, key, symtab); if (!error && type->value.type != OVSDB_TYPE_VOID) { *s = skip_spaces(*s); if (**s == '=') { (*s)++; *s = skip_spaces(*s); - error = parse_atom_token(s, &type->value, value); + error = parse_atom_token(s, &type->value, value, symtab); } else { error = xasprintf("%s: syntax error at \"%c\" expecting \"=\"", start, **s); @@ -1080,10 +1088,14 @@ free_key_value(const struct ovsdb_type *type, * or, for a map, '='-delimited pairs of atoms. Each atom must in a format * acceptable to ovsdb_atom_from_string(). Optionally, a set may be enclosed * in "[]" or a map in "{}"; for an empty set or map these punctuators are - * required. */ + * required. + * + * Optionally, a symbol table may be supplied as 'symtab'. It is passed to + * ovsdb_atom_to_string(). */ char * ovsdb_datum_from_string(struct ovsdb_datum *datum, - const struct ovsdb_type *type, const char *s) + const struct ovsdb_type *type, const char *s, + struct ovsdb_symbol_table *symtab) { bool is_map = ovsdb_type_is_map(type); struct ovsdb_error *dberror; @@ -1118,7 +1130,7 @@ ovsdb_datum_from_string(struct ovsdb_datum *datum, } /* Add to datum. */ - error = parse_key_value(&p, type, &key, &value); + error = parse_key_value(&p, type, &key, &value, symtab); if (error) { goto error; } @@ -1549,6 +1561,21 @@ ovsdb_symbol_table_insert(struct ovsdb_symbol_table *symtab, } return symbol; } + +const char * +ovsdb_symbol_table_find_unused(const struct ovsdb_symbol_table *symtab) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &symtab->sh) { + struct ovsdb_symbol *symbol = node->data; + if (!symbol->used) { + return node->name; + } + } + + return NULL; +} /* Extracts a token from the beginning of 's' and returns a pointer just after * the token. Stores the token itself into '*outp', which the caller is diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h index a5c49f97..e12153c9 100644 --- a/lib/ovsdb-data.h +++ b/lib/ovsdb-data.h @@ -75,7 +75,8 @@ struct json *ovsdb_atom_to_json(const union ovsdb_atom *, enum ovsdb_atomic_type); char *ovsdb_atom_from_string(union ovsdb_atom *, - const struct ovsdb_base_type *, const char *) + const struct ovsdb_base_type *, const char *, + struct ovsdb_symbol_table *) WARN_UNUSED_RESULT; void ovsdb_atom_to_string(const union ovsdb_atom *, enum ovsdb_atomic_type, struct ds *); @@ -142,7 +143,8 @@ struct json *ovsdb_datum_to_json(const struct ovsdb_datum *, const struct ovsdb_type *); char *ovsdb_datum_from_string(struct ovsdb_datum *, - const struct ovsdb_type *, const char *) + const struct ovsdb_type *, const char *, + struct ovsdb_symbol_table *) WARN_UNUSED_RESULT; void ovsdb_datum_to_string(const struct ovsdb_datum *, const struct ovsdb_type *, struct ds *); @@ -216,6 +218,7 @@ struct ovsdb_symbol *ovsdb_symbol_table_put(struct ovsdb_symbol_table *, const struct uuid *, bool used); struct ovsdb_symbol *ovsdb_symbol_table_insert(struct ovsdb_symbol_table *, const char *name); +const char *ovsdb_symbol_table_find_unused(const struct ovsdb_symbol_table *); /* Tokenization * diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c index 94dd466b..ca77cc2e 100644 --- a/lib/ovsdb-idl.c +++ b/lib/ovsdb-idl.c @@ -1460,10 +1460,18 @@ ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) const struct ovsdb_idl_row * ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn, - const struct ovsdb_idl_table_class *class) + const struct ovsdb_idl_table_class *class, + const struct uuid *uuid) { struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class); - uuid_generate(&row->uuid); + + if (uuid) { + 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 = xmalloc(class->n_columns * sizeof *row->new); row->written = bitmap_allocate(class->n_columns); diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h index 2aaaa71c..31fae984 100644 --- a/lib/ovsdb-idl.h +++ b/lib/ovsdb-idl.h @@ -84,7 +84,8 @@ void ovsdb_idl_txn_write(const struct ovsdb_idl_row *, struct ovsdb_datum *); 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 *); + struct ovsdb_idl_txn *, const struct ovsdb_idl_table_class *, + const struct uuid *); struct ovsdb_idl *ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *); diff --git a/lib/shash.c b/lib/shash.c index caac460e..1a85b792 100644 --- a/lib/shash.c +++ b/lib/shash.c @@ -79,15 +79,23 @@ shash_count(const struct shash *shash) /* It is the caller's responsibility to avoid duplicate names, if that is * desirable. */ struct shash_node * -shash_add(struct shash *sh, const char *name, const void *data) +shash_add_nocopy(struct shash *sh, char *name, const void *data) { struct shash_node *node = xmalloc(sizeof *node); - node->name = xstrdup(name); + node->name = name; node->data = (void *) data; hmap_insert(&sh->map, &node->node, hash_name(name)); return node; } +/* It is the caller's responsibility to avoid duplicate names, if that is + * desirable. */ +struct shash_node * +shash_add(struct shash *sh, const char *name, const void *data) +{ + return shash_add_nocopy(sh, xstrdup(name), data); +} + bool shash_add_once(struct shash *sh, const char *name, const void *data) { diff --git a/lib/shash.h b/lib/shash.h index 38d0dbbb..de850452 100644 --- a/lib/shash.h +++ b/lib/shash.h @@ -50,6 +50,7 @@ void shash_clear(struct shash *); bool shash_is_empty(const struct shash *); size_t shash_count(const struct shash *); struct shash_node *shash_add(struct shash *, const char *, const void *); +struct shash_node *shash_add_nocopy(struct shash *, char *, const void *); bool shash_add_once(struct shash *, const char *, const void *); void shash_add_assert(struct shash *, const char *, const void *); void shash_delete(struct shash *, struct shash_node *); diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in index c89048ee..c0103488 100755 --- a/ovsdb/ovsdb-idlc.in +++ b/ovsdb/ovsdb-idlc.in @@ -378,7 +378,7 @@ void struct %(s)s * %(s)s_insert(struct ovsdb_idl_txn *txn) { - return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s])); + return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s], NULL)); } ''' % {'s': structName, 'p': prefix, diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c index 672759be..21825d74 100644 --- a/tests/test-ovsdb.c +++ b/tests/test-ovsdb.c @@ -396,7 +396,7 @@ do_parse_atom_strings(int argc, char *argv[]) union ovsdb_atom atom; struct ds out; - die_if_error(ovsdb_atom_from_string(&atom, &base, argv[i])); + die_if_error(ovsdb_atom_from_string(&atom, &base, argv[i], NULL)); ds_init(&out); ovsdb_atom_to_string(&atom, base.type, &out); @@ -448,7 +448,7 @@ do_parse_data_strings(int argc, char *argv[]) struct ovsdb_datum datum; struct ds out; - die_if_error(ovsdb_datum_from_string(&datum, &type, argv[i])); + die_if_error(ovsdb_datum_from_string(&datum, &type, argv[i], NULL)); ds_init(&out); ovsdb_datum_to_string(&datum, &type, &out); diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in index ac4cf451..ab487ad4 100644 --- a/utilities/ovs-vsctl.8.in +++ b/utilities/ovs-vsctl.8.in @@ -487,8 +487,10 @@ the syntax is that of strings in JSON, e.g. backslashes may be used to escape special characters. The empty string must be represented as a pair of double quotes (\fB""\fR). .IP "UUID" -A universally unique identifier in the style of RFC 4122, -e.g. \fBf81d4fae-7dec-11d0-a765-00a0c91e6bf6\fR. +Either a universally unique identifier in the style of RFC 4122, +e.g. \fBf81d4fae\-7dec\-11d0\-a765\-00a0c91e6bf6\fR, or an \fB@\fIname\fR +defined by the \fBcreate\fR command within the same \fBovs\-vsctl\fR +invocation. .PP Multiple values in a single column may be separated by spaces or a single comma. When multiple values are present, duplicates are not @@ -559,10 +561,15 @@ Sets each \fIcolumn\fR in \fIrecord\fR in \fItable\fR to the empty set or empty map, as appropriate. This command applies only to columns that are allowed to be empty. . -.IP "\fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..." +.IP "[\fB--id=@\fIname\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. +.IP +If \fB@\fIname\fR is specified, then the UUID for the new row may be +referred to by that name elsewhere in the same \fBovs\-vsctl\fR +invocation in contexts where a UUID is expected. Such references may +precede or follow the \fBcreate\fR command. . .IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..." Deletes each specified \fIrecord\fR from \fItable\fR. Unless @@ -586,6 +593,12 @@ Delete bridge \fBbr0\fR if it exists (the \fB\-\-\fR is required to separate \fBdel\-br\fR's options from the global options): .IP .B "ovs\-vsctl \-\- \-\-if\-exists del\-br br0" +.PP +Set the \fBqos\fR column of the \fBPort\fR record for \fBeth0\fR to +point to a new \fBQoS\fR record, which in turn points with its queue 0 +to a new \fBQueue\fR record: +.IP +.B "ovs-vsctl \-\- set port eth0 qos=@newqos \-\- \-\-id=@newqos create qos type=linux\-htb other\-config:max\-rate=1000000 queues:0=@newqueue \-\- \-\-id=@newqueue create queue other\-config:min\-rate=1000000 other\-config:max\-rate=1000000" . .SH "EXIT STATUS" .IP "0" @@ -599,3 +612,4 @@ bridge that does not exist. . .BR ovsdb\-server (1), .BR ovs\-vswitchd (8). +\ diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c index 9b401977..76a3831f 100644 --- a/utilities/ovs-vsctl.c +++ b/utilities/ovs-vsctl.c @@ -111,7 +111,8 @@ static void do_vsctl(const char *args, static const struct vsctl_table_class *get_table(const char *table_name); static void set_column(const struct vsctl_table_class *, - const struct ovsdb_idl_row *, const char *arg); + const struct ovsdb_idl_row *, const char *arg, + struct ovsdb_symbol_table *); int @@ -511,6 +512,7 @@ struct vsctl_context { struct ds output; struct ovsdb_idl *idl; struct ovsdb_idl_txn *txn; + struct ovsdb_symbol_table *symtab; const struct ovsrec_open_vswitch *ovs; }; @@ -1384,7 +1386,8 @@ add_port(struct vsctl_context *ctx, } for (i = 0; i < n_settings; i++) { - set_column(get_table("Port"), &port->header_, settings[i]); + set_column(get_table("Port"), &port->header_, settings[i], + ctx->symtab); } bridge_insert_port((bridge->parent ? bridge->parent->br_cfg @@ -2229,7 +2232,7 @@ cmd_get(struct vsctl_context *ctx) die_if_error(ovsdb_atom_from_string(&key, &column->type.key, - key_string)); + key_string, ctx->symtab)); idx = ovsdb_datum_find_key(&datum, &key, column->type.key.type); @@ -2309,7 +2312,8 @@ cmd_list(struct vsctl_context *ctx) static void set_column(const struct vsctl_table_class *table, - const struct ovsdb_idl_row *row, const char *arg) + const struct ovsdb_idl_row *row, const char *arg, + struct ovsdb_symbol_table *symtab) { const struct ovsdb_idl_column *column; char *key_string, *value_string; @@ -2332,9 +2336,9 @@ set_column(const struct vsctl_table_class *table, } die_if_error(ovsdb_atom_from_string(&key, &column->type.key, - key_string)); + key_string, symtab)); die_if_error(ovsdb_atom_from_string(&value, &column->type.value, - value_string)); + value_string, symtab)); ovsdb_datum_init_empty(&new); ovsdb_datum_add_unsafe(&new, &key, &value, &column->type); @@ -2351,7 +2355,7 @@ set_column(const struct vsctl_table_class *table, struct ovsdb_datum datum; die_if_error(ovsdb_datum_from_string(&datum, &column->type, - value_string)); + value_string, symtab)); ovsdb_idl_txn_write(row, column, &datum); } @@ -2371,7 +2375,7 @@ cmd_set(struct vsctl_context *ctx) table = get_table(table_name); row = must_get_row(ctx, table, record_id); for (i = 3; i < ctx->argc; i++) { - set_column(table, row, ctx->argv[i]); + set_column(table, row, ctx->argv[i], ctx->symtab); } } @@ -2401,7 +2405,8 @@ cmd_add(struct vsctl_context *ctx) add_type = *type; add_type.n_min = 1; add_type.n_max = UINT_MAX; - die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i])); + die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i], + ctx->symtab)); ovsdb_datum_union(&old, &add, type, false); ovsdb_datum_destroy(&add, type); } @@ -2442,11 +2447,13 @@ cmd_remove(struct vsctl_context *ctx) rm_type = *type; rm_type.n_min = 1; rm_type.n_max = UINT_MAX; - error = ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i]); + error = ovsdb_datum_from_string(&rm, &rm_type, + ctx->argv[i], ctx->symtab); if (error && ovsdb_type_is_map(&rm_type)) { free(error); rm_type.value.type = OVSDB_TYPE_VOID; - die_if_error(ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i])); + die_if_error(ovsdb_datum_from_string(&rm, &rm_type, + ctx->argv[i], ctx->symtab)); } ovsdb_datum_subtract(&old, type, &rm, &rm_type); ovsdb_datum_destroy(&rm, &rm_type); @@ -2494,15 +2501,36 @@ cmd_clear(struct vsctl_context *ctx) static void cmd_create(struct vsctl_context *ctx) { + const char *id = shash_find_data(&ctx->options, "--id"); const char *table_name = ctx->argv[1]; const struct vsctl_table_class *table; const struct ovsdb_idl_row *row; + const struct uuid *uuid; int i; + if (id) { + struct ovsdb_symbol *symbol; + + if (id[0] != '@') { + vsctl_fatal("row id \"%s\" does not begin with \"@\"", id); + } + + symbol = ovsdb_symbol_table_insert(ctx->symtab, id); + if (symbol->used) { + vsctl_fatal("row id \"%s\" may only be used to insert a single " + "row", id); + } + symbol->used = true; + + uuid = &symbol->uuid; + } else { + uuid = NULL; + } + table = get_table(table_name); - row = ovsdb_idl_txn_insert(ctx->txn, table->class); + row = ovsdb_idl_txn_insert(ctx->txn, table->class, uuid); for (i = 2; i < ctx->argc; i++) { - set_column(table, row, ctx->argv[i]); + set_column(table, row, ctx->argv[i], ctx->symtab); } ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid)); } @@ -2567,7 +2595,8 @@ where_uuid_equals(const struct uuid *uuid) static void vsctl_context_init(struct vsctl_context *ctx, struct vsctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, - const struct ovsrec_open_vswitch *ovs) + const struct ovsrec_open_vswitch *ovs, + struct ovsdb_symbol_table *symtab) { ctx->argc = command->argc; ctx->argv = command->argv; @@ -2577,7 +2606,7 @@ vsctl_context_init(struct vsctl_context *ctx, struct vsctl_command *command, ctx->idl = idl; ctx->txn = txn; ctx->ovs = ovs; - + ctx->symtab = symtab; } static void @@ -2593,6 +2622,8 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, struct ovsdb_idl_txn *txn; const struct ovsrec_open_vswitch *ovs; enum ovsdb_idl_txn_status status; + struct ovsdb_symbol_table *symtab; + const char *unused; struct vsctl_command *c; int64_t next_cfg = 0; char *error; @@ -2616,11 +2647,12 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, json_destroy(where); } + symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { struct vsctl_context ctx; ds_init(&c->output); - vsctl_context_init(&ctx, c, idl, txn, ovs); + vsctl_context_init(&ctx, c, idl, txn, ovs, symtab); (c->syntax->run)(&ctx); vsctl_context_done(&ctx, c); } @@ -2634,7 +2666,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, if (c->syntax->postprocess) { struct vsctl_context ctx; - vsctl_context_init(&ctx, c, idl, txn, ovs); + vsctl_context_init(&ctx, c, idl, txn, ovs, symtab); (c->syntax->postprocess)(&ctx); vsctl_context_done(&ctx, c); } @@ -2644,6 +2676,13 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, ovsdb_idl_txn_destroy(txn); the_idl_txn = NULL; + unused = ovsdb_symbol_table_find_unused(symtab); + if (unused) { + vsctl_fatal("row id \"%s\" is referenced but never created (e.g. " + "with \"-- --id=%s create ...\")", unused, unused); + } + ovsdb_symbol_table_destroy(symtab); + switch (status) { case TXN_INCOMPLETE: NOT_REACHED(); @@ -2673,6 +2712,8 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; + struct shash_node *node; + if (oneline) { size_t j; @@ -2697,6 +2738,10 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); + + SHASH_FOR_EACH (node, &c->options) { + free(node->data); + } shash_destroy(&c->options); } free(commands); @@ -2769,7 +2814,7 @@ static const struct vsctl_command_syntax all_commands[] = { {"add", 4, INT_MAX, cmd_add, NULL, ""}, {"remove", 4, INT_MAX, cmd_remove, NULL, ""}, {"clear", 3, INT_MAX, cmd_clear, NULL, ""}, - {"create", 2, INT_MAX, cmd_create, post_create, ""}, + {"create", 2, INT_MAX, cmd_create, post_create, "--id="}, {"destroy", 1, INT_MAX, cmd_destroy, NULL, "--if-exists"}, {NULL, 0, 0, NULL, NULL, NULL}, -- 2.30.2