2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-29 05:18:13 +00:00
ovs/lib/ovsdb-data.c
Ben Pfaff c532bf9dd4 ovsdb: Save some space in the log for newly inserted records.
When a new record is inserted into a database, ovsdb logs the values of all
of the fields in the record.  However, often new records have many columns
that contain default values.  There is no need to log those values, so this
commit causes them to be omitted.

As a side effect, this also makes "ovsdb-tool show-log --more --more"
output easier to read, because record insertions print less noise.  (Adding
--more --more to this command makes it print changes to database records.
The --more option will be introduced in an upcoming commit.)
2010-01-11 13:14:54 -08:00

900 lines
24 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (c) 2009, 2010 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "ovsdb-data.h"
#include <assert.h>
#include <limits.h>
#include "hash.h"
#include "ovsdb-error.h"
#include "json.h"
#include "shash.h"
#include "sort.h"
static struct json *
wrap_json(const char *name, struct json *wrapped)
{
return json_array_create_2(json_string_create(name), wrapped);
}
void
ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
atom->integer = 0;
break;
case OVSDB_TYPE_REAL:
atom->real = 0.0;
break;
case OVSDB_TYPE_BOOLEAN:
atom->boolean = false;
break;
case OVSDB_TYPE_STRING:
atom->string = xmemdup("", 1);
break;
case OVSDB_TYPE_UUID:
uuid_zero(&atom->uuid);
break;
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
}
bool
ovsdb_atom_is_default(const union ovsdb_atom *atom,
enum ovsdb_atomic_type type)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
return atom->integer == 0;
case OVSDB_TYPE_REAL:
return atom->real == 0.0;
case OVSDB_TYPE_BOOLEAN:
return atom->boolean == false;
case OVSDB_TYPE_STRING:
return atom->string[0] == '\0';
case OVSDB_TYPE_UUID:
return uuid_is_zero(&atom->uuid);
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
}
void
ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old,
enum ovsdb_atomic_type type)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
new->integer = old->integer;
break;
case OVSDB_TYPE_REAL:
new->real = old->real;
break;
case OVSDB_TYPE_BOOLEAN:
new->boolean = old->boolean;
break;
case OVSDB_TYPE_STRING:
new->string = xstrdup(old->string);
break;
case OVSDB_TYPE_UUID:
new->uuid = old->uuid;
break;
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
}
void
ovsdb_atom_swap(union ovsdb_atom *a, union ovsdb_atom *b)
{
union ovsdb_atom tmp = *a;
*a = *b;
*b = tmp;
}
uint32_t
ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
uint32_t basis)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
return hash_int(atom->integer, basis);
case OVSDB_TYPE_REAL:
return hash_double(atom->real, basis);
case OVSDB_TYPE_BOOLEAN:
return hash_boolean(atom->boolean, basis);
case OVSDB_TYPE_STRING:
return hash_string(atom->string, basis);
case OVSDB_TYPE_UUID:
return hash_int(uuid_hash(&atom->uuid), basis);
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
}
int
ovsdb_atom_compare_3way(const union ovsdb_atom *a,
const union ovsdb_atom *b,
enum ovsdb_atomic_type type)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
return a->integer < b->integer ? -1 : a->integer > b->integer;
case OVSDB_TYPE_REAL:
return a->real < b->real ? -1 : a->real > b->real;
case OVSDB_TYPE_BOOLEAN:
return a->boolean - b->boolean;
case OVSDB_TYPE_STRING:
return strcmp(a->string, b->string);
case OVSDB_TYPE_UUID:
return uuid_compare_3way(&a->uuid, &b->uuid);
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
}
static struct ovsdb_error *
unwrap_json(const struct json *json, const char *name,
enum json_type value_type, const struct json **value)
{
if (json->type != JSON_ARRAY
|| json->u.array.n != 2
|| json->u.array.elems[0]->type != JSON_STRING
|| (name && strcmp(json->u.array.elems[0]->u.string, name))
|| json->u.array.elems[1]->type != value_type)
{
return ovsdb_syntax_error(json, NULL, "expected [\"%s\", <%s>]", name,
json_type_to_string(value_type));
}
*value = json->u.array.elems[1];
return NULL;
}
static struct ovsdb_error *
parse_json_pair(const struct json *json,
const struct json **elem0, const struct json **elem1)
{
if (json->type != JSON_ARRAY || json->u.array.n != 2) {
return ovsdb_syntax_error(json, NULL, "expected 2-element array");
}
*elem0 = json->u.array.elems[0];
*elem1 = json->u.array.elems[1];
return NULL;
}
static struct ovsdb_error *
ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
const struct ovsdb_symbol_table *symtab)
WARN_UNUSED_RESULT;
static struct ovsdb_error *
ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
const struct ovsdb_symbol_table *symtab)
{
struct ovsdb_error *error0;
const struct json *value;
error0 = unwrap_json(json, "uuid", JSON_STRING, &value);
if (!error0) {
const char *uuid_string = json_string(value);
if (!uuid_from_string(uuid, uuid_string)) {
return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID",
uuid_string);
}
} else if (symtab) {
struct ovsdb_error *error1;
error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value);
if (!error1) {
const char *name = json_string(value);
const struct ovsdb_symbol *symbol;
ovsdb_error_destroy(error0);
symbol = ovsdb_symbol_table_get(symtab, name);
if (symbol) {
*uuid = symbol->uuid;
return NULL;
} else {
return ovsdb_syntax_error(json, NULL,
"unknown named-uuid \"%s\"", name);
}
}
ovsdb_error_destroy(error1);
}
return error0;
}
struct ovsdb_error *
ovsdb_atom_from_json(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
const struct json *json,
const struct ovsdb_symbol_table *symtab)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
if (json->type == JSON_INTEGER) {
atom->integer = json->u.integer;
return NULL;
}
break;
case OVSDB_TYPE_REAL:
if (json->type == JSON_INTEGER) {
atom->real = json->u.integer;
return NULL;
} else if (json->type == JSON_REAL) {
atom->real = json->u.real;
return NULL;
}
break;
case OVSDB_TYPE_BOOLEAN:
if (json->type == JSON_TRUE) {
atom->boolean = true;
return NULL;
} else if (json->type == JSON_FALSE) {
atom->boolean = false;
return NULL;
}
break;
case OVSDB_TYPE_STRING:
if (json->type == JSON_STRING) {
atom->string = xstrdup(json->u.string);
return NULL;
}
break;
case OVSDB_TYPE_UUID:
return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab);
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
return ovsdb_syntax_error(json, NULL, "expected %s",
ovsdb_atomic_type_to_string(type));
}
struct json *
ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
{
switch (type) {
case OVSDB_TYPE_VOID:
NOT_REACHED();
case OVSDB_TYPE_INTEGER:
return json_integer_create(atom->integer);
case OVSDB_TYPE_REAL:
return json_real_create(atom->real);
case OVSDB_TYPE_BOOLEAN:
return json_boolean_create(atom->boolean);
case OVSDB_TYPE_STRING:
return json_string_create(atom->string);
case OVSDB_TYPE_UUID:
return wrap_json("uuid", json_string_create_nocopy(
xasprintf(UUID_FMT, UUID_ARGS(&atom->uuid))));
case OVSDB_N_TYPES:
default:
NOT_REACHED();
}
}
static union ovsdb_atom *
alloc_default_atoms(enum ovsdb_atomic_type type, size_t n)
{
if (type != OVSDB_TYPE_VOID && n) {
union ovsdb_atom *atoms;
unsigned int i;
atoms = xmalloc(n * sizeof *atoms);
for (i = 0; i < n; i++) {
ovsdb_atom_init_default(&atoms[i], type);
}
return atoms;
} else {
/* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
* treated as xmalloc(1). */
return NULL;
}
}
void
ovsdb_datum_init_default(struct ovsdb_datum *datum,
const struct ovsdb_type *type)
{
datum->n = type->n_min;
datum->keys = alloc_default_atoms(type->key_type, datum->n);
datum->values = alloc_default_atoms(type->value_type, datum->n);
}
bool
ovsdb_datum_is_default(const struct ovsdb_datum *datum,
const struct ovsdb_type *type)
{
size_t i;
if (datum->n != type->n_min) {
return false;
}
for (i = 0; i < datum->n; i++) {
if (!ovsdb_atom_is_default(&datum->keys[i], type->key_type)) {
return false;
}
if (type->value_type != OVSDB_TYPE_VOID
&& !ovsdb_atom_is_default(&datum->values[i], type->value_type)) {
return false;
}
}
return true;
}
static union ovsdb_atom *
clone_atoms(const union ovsdb_atom *old, enum ovsdb_atomic_type type, size_t n)
{
if (type != OVSDB_TYPE_VOID && n) {
union ovsdb_atom *new;
unsigned int i;
new = xmalloc(n * sizeof *new);
for (i = 0; i < n; i++) {
ovsdb_atom_clone(&new[i], &old[i], type);
}
return new;
} else {
/* Avoid wasting memory in the n == 0 case, because xmalloc(0) is
* treated as xmalloc(1). */
return NULL;
}
}
void
ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old,
const struct ovsdb_type *type)
{
unsigned int n = old->n;
new->n = n;
new->keys = clone_atoms(old->keys, type->key_type, n);
new->values = clone_atoms(old->values, type->value_type, n);
}
static void
free_data(enum ovsdb_atomic_type type,
union ovsdb_atom *atoms, size_t n_atoms)
{
if (ovsdb_atom_needs_destruction(type)) {
unsigned int i;
for (i = 0; i < n_atoms; i++) {
ovsdb_atom_destroy(&atoms[i], type);
}
}
free(atoms);
}
void
ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type)
{
free_data(type->key_type, datum->keys, datum->n);
free_data(type->value_type, datum->values, datum->n);
}
void
ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b)
{
struct ovsdb_datum tmp = *a;
*a = *b;
*b = tmp;
}
struct ovsdb_datum_sort_cbdata {
const struct ovsdb_type *type;
struct ovsdb_datum *datum;
};
static int
ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_)
{
struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
return ovsdb_atom_compare_3way(&cbdata->datum->keys[a],
&cbdata->datum->keys[b],
cbdata->type->key_type);
}
static void
ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_)
{
struct ovsdb_datum_sort_cbdata *cbdata = cbdata_;
ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]);
if (cbdata->type->value_type != OVSDB_TYPE_VOID) {
ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]);
}
}
struct ovsdb_error *
ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type)
{
if (datum->n < 2) {
return NULL;
} else {
struct ovsdb_datum_sort_cbdata cbdata;
size_t i;
cbdata.type = type;
cbdata.datum = datum;
sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb,
&cbdata);
for (i = 0; i < datum->n - 1; i++) {
if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1],
type->key_type)) {
if (ovsdb_type_is_map(type)) {
return ovsdb_error(NULL, "map contains duplicate key");
} else {
return ovsdb_error(NULL, "set contains duplicate");
}
}
}
return NULL;
}
}
struct ovsdb_error *
ovsdb_datum_from_json(struct ovsdb_datum *datum,
const struct ovsdb_type *type,
const struct json *json,
const struct ovsdb_symbol_table *symtab)
{
struct ovsdb_error *error;
if (ovsdb_type_is_scalar(type)) {
datum->n = 1;
datum->keys = xmalloc(sizeof *datum->keys);
datum->values = NULL;
error = ovsdb_atom_from_json(&datum->keys[0], type->key_type,
json, symtab);
if (error) {
free(datum->keys);
}
return error;
} else {
bool is_map = ovsdb_type_is_map(type);
const char *class = is_map ? "map" : "set";
const struct json *inner;
unsigned int i;
size_t n;
assert(is_map || ovsdb_type_is_set(type));
error = unwrap_json(json, class, JSON_ARRAY, &inner);
if (error) {
return error;
}
n = inner->u.array.n;
if (n < type->n_min || n > type->n_max) {
return ovsdb_syntax_error(json, NULL, "%s must have %u to "
"%u members but %zu are present",
class, type->n_min, type->n_max, n);
}
datum->n = 0;
datum->keys = xmalloc(n * sizeof *datum->keys);
datum->values = is_map ? xmalloc(n * sizeof *datum->values) : NULL;
for (i = 0; i < n; i++) {
const struct json *element = inner->u.array.elems[i];
const struct json *key = NULL;
const struct json *value = NULL;
if (!is_map) {
key = element;
} else {
error = parse_json_pair(element, &key, &value);
if (error) {
goto error;
}
}
error = ovsdb_atom_from_json(&datum->keys[i], type->key_type,
key, symtab);
if (error) {
goto error;
}
if (is_map) {
error = ovsdb_atom_from_json(&datum->values[i],
type->value_type, value, symtab);
if (error) {
ovsdb_atom_destroy(&datum->keys[i], type->key_type);
goto error;
}
}
datum->n++;
}
error = ovsdb_datum_sort(datum, type);
if (error) {
goto error;
}
return NULL;
error:
ovsdb_datum_destroy(datum, type);
return error;
}
}
struct json *
ovsdb_datum_to_json(const struct ovsdb_datum *datum,
const struct ovsdb_type *type)
{
/* These tests somewhat tolerate a 'datum' that does not exactly match
* 'type', in particular a datum with 'n' not in the allowed range. */
if (datum->n == 1 && ovsdb_type_is_scalar(type)) {
return ovsdb_atom_to_json(&datum->keys[0], type->key_type);
} else if (type->value_type == OVSDB_TYPE_VOID) {
struct json **elems;
size_t i;
elems = xmalloc(datum->n * sizeof *elems);
for (i = 0; i < datum->n; i++) {
elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key_type);
}
return wrap_json("set", json_array_create(elems, datum->n));
} else {
struct json **elems;
size_t i;
elems = xmalloc(datum->n * sizeof *elems);
for (i = 0; i < datum->n; i++) {
elems[i] = json_array_create_2(
ovsdb_atom_to_json(&datum->keys[i], type->key_type),
ovsdb_atom_to_json(&datum->values[i], type->value_type));
}
return wrap_json("map", json_array_create(elems, datum->n));
}
}
static uint32_t
hash_atoms(enum ovsdb_atomic_type type, const union ovsdb_atom *atoms,
unsigned int n, uint32_t basis)
{
if (type != OVSDB_TYPE_VOID) {
unsigned int i;
for (i = 0; i < n; i++) {
basis = ovsdb_atom_hash(&atoms[i], type, basis);
}
}
return basis;
}
uint32_t
ovsdb_datum_hash(const struct ovsdb_datum *datum,
const struct ovsdb_type *type, uint32_t basis)
{
basis = hash_atoms(type->key_type, datum->keys, datum->n, basis);
basis ^= (type->key_type << 24) | (type->value_type << 16) | datum->n;
basis = hash_atoms(type->value_type, datum->values, datum->n, basis);
return basis;
}
static int
atom_arrays_compare_3way(const union ovsdb_atom *a,
const union ovsdb_atom *b,
enum ovsdb_atomic_type type,
size_t n)
{
unsigned int i;
for (i = 0; i < n; i++) {
int cmp = ovsdb_atom_compare_3way(&a[i], &b[i], type);
if (cmp) {
return cmp;
}
}
return 0;
}
bool
ovsdb_datum_equals(const struct ovsdb_datum *a,
const struct ovsdb_datum *b,
const struct ovsdb_type *type)
{
return !ovsdb_datum_compare_3way(a, b, type);
}
int
ovsdb_datum_compare_3way(const struct ovsdb_datum *a,
const struct ovsdb_datum *b,
const struct ovsdb_type *type)
{
int cmp;
if (a->n != b->n) {
return a->n < b->n ? -1 : 1;
}
cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key_type, a->n);
if (cmp) {
return cmp;
}
return (type->value_type == OVSDB_TYPE_VOID ? 0
: atom_arrays_compare_3way(a->values, b->values, type->value_type,
a->n));
}
/* If atom 'i' in 'a' is also in 'b', returns its index in 'b', otherwise
* UINT_MAX. 'type' must be the type of 'a' and 'b', except that
* type->value_type may be set to OVSDB_TYPE_VOID to compare keys but not
* values. */
static unsigned int
ovsdb_datum_find(const struct ovsdb_datum *a, int i,
const struct ovsdb_datum *b,
const struct ovsdb_type *type)
{
int low = 0;
int high = b->n;
while (low < high) {
int j = (low + high) / 2;
int cmp = ovsdb_atom_compare_3way(&a->keys[i], &b->keys[j],
type->key_type);
if (cmp < 0) {
high = j;
} else if (cmp > 0) {
low = j + 1;
} else {
bool eq_value = (type->value_type == OVSDB_TYPE_VOID
|| ovsdb_atom_equals(&a->values[i], &b->values[j],
type->value_type));
return eq_value ? j : UINT_MAX;
}
}
return UINT_MAX;
}
/* Returns true if every element in 'a' is also in 'b', false otherwise. */
bool
ovsdb_datum_includes_all(const struct ovsdb_datum *a,
const struct ovsdb_datum *b,
const struct ovsdb_type *type)
{
size_t i;
for (i = 0; i < a->n; i++) {
if (ovsdb_datum_find(a, i, b, type) == UINT_MAX) {
return false;
}
}
return true;
}
/* Returns true if no element in 'a' is also in 'b', false otherwise. */
bool
ovsdb_datum_excludes_all(const struct ovsdb_datum *a,
const struct ovsdb_datum *b,
const struct ovsdb_type *type)
{
size_t i;
for (i = 0; i < a->n; i++) {
if (ovsdb_datum_find(a, i, b, type) != UINT_MAX) {
return false;
}
}
return true;
}
static void
ovsdb_datum_reallocate(struct ovsdb_datum *a, const struct ovsdb_type *type,
unsigned int capacity)
{
a->keys = xrealloc(a->keys, capacity * sizeof *a->keys);
if (type->value_type != OVSDB_TYPE_VOID) {
a->values = xrealloc(a->values, capacity * sizeof *a->values);
}
}
static void
ovsdb_datum_remove(struct ovsdb_datum *a, size_t i,
const struct ovsdb_type *type)
{
ovsdb_atom_destroy(&a->keys[i], type->key_type);
a->keys[i] = a->keys[a->n - 1];
if (type->value_type != OVSDB_TYPE_VOID) {
ovsdb_atom_destroy(&a->values[i], type->value_type);
a->values[i] = a->values[a->n - 1];
}
a->n--;
}
void
ovsdb_datum_union(struct ovsdb_datum *a,
const struct ovsdb_datum *b, const struct ovsdb_type *type)
{
struct ovsdb_type type_without_value;
unsigned int n;
size_t i;
type_without_value = *type;
type_without_value.value_type = OVSDB_TYPE_VOID;
n = a->n;
for (i = 0; i < b->n; i++) {
if (ovsdb_datum_find(b, i, a, &type_without_value) == UINT_MAX) {
if (n == a->n) {
ovsdb_datum_reallocate(a, type, a->n + (b->n - i));
}
ovsdb_atom_clone(&a->keys[n], &b->keys[i], type->key_type);
if (type->value_type != OVSDB_TYPE_VOID) {
ovsdb_atom_clone(&a->values[n], &b->values[i],
type->value_type);
}
n++;
}
}
if (n != a->n) {
struct ovsdb_error *error;
a->n = n;
error = ovsdb_datum_sort(a, type);
assert(!error);
}
}
void
ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type,
const struct ovsdb_datum *b,
const struct ovsdb_type *b_type)
{
bool changed = false;
size_t i;
assert(a_type->key_type == b_type->key_type);
assert(a_type->value_type == b_type->value_type
|| b_type->value_type == OVSDB_TYPE_VOID);
/* XXX The big-O of this could easily be improved. */
for (i = 0; i < a->n; ) {
unsigned int idx = ovsdb_datum_find(a, i, b, b_type);
if (idx != UINT_MAX) {
changed = true;
ovsdb_datum_remove(a, i, a_type);
} else {
i++;
}
}
if (changed) {
struct ovsdb_error *error = ovsdb_datum_sort(a, a_type);
assert(!error);
}
}
struct ovsdb_symbol_table {
struct shash sh;
};
struct ovsdb_symbol_table *
ovsdb_symbol_table_create(void)
{
struct ovsdb_symbol_table *symtab = xmalloc(sizeof *symtab);
shash_init(&symtab->sh);
return symtab;
}
void
ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *symtab)
{
if (symtab) {
struct shash_node *node, *next;
SHASH_FOR_EACH_SAFE (node, next, &symtab->sh) {
struct ovsdb_symbol *symbol = node->data;
free(symbol);
shash_delete(&symtab->sh, node);
}
shash_destroy(&symtab->sh);
free(symtab);
}
}
struct ovsdb_symbol *
ovsdb_symbol_table_get(const struct ovsdb_symbol_table *symtab,
const char *name)
{
return shash_find_data(&symtab->sh, name);
}
void
ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name,
const struct uuid *uuid, bool used)
{
struct ovsdb_symbol *symbol;
assert(!ovsdb_symbol_table_get(symtab, name));
symbol = xmalloc(sizeof *symbol);
symbol->uuid = *uuid;
symbol->used = used;
shash_add(&symtab->sh, name, symbol);
}