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:
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;
* - 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;
}
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;
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);
* 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;
}
/* Add to datum. */
- error = parse_key_value(&p, type, &key, &value);
+ error = parse_key_value(&p, type, &key, &value, symtab);
if (error) {
goto error;
}
}
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;
+}
\f
/* 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
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 *);
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 *);
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 *);
\f
/* Tokenization
*
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);
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 *);
/* 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)
{
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 *);
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,
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);
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);
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
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
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"
.
.BR ovsdb\-server (1),
.BR ovs\-vswitchd (8).
+\
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
struct ds output;
struct ovsdb_idl *idl;
struct ovsdb_idl_txn *txn;
+ struct ovsdb_symbol_table *symtab;
const struct ovsrec_open_vswitch *ovs;
};
}
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
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);
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;
}
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);
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);
}
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);
}
}
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);
}
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);
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));
}
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;
ctx->idl = idl;
ctx->txn = txn;
ctx->ovs = ovs;
-
+ ctx->symtab = symtab;
}
static void
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;
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);
}
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);
}
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();
for (c = commands; c < &commands[n_commands]; c++) {
struct ds *ds = &c->output;
+ struct shash_node *node;
+
if (oneline) {
size_t j;
fputs(ds_cstr(ds), stdout);
}
ds_destroy(&c->output);
+
+ SHASH_FOR_EACH (node, &c->options) {
+ free(node->data);
+ }
shash_destroy(&c->options);
}
free(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},