2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-02 23:35:27 +00:00

Initial implementation of OVSDB.

This commit is contained in:
Ben Pfaff
2009-11-04 15:11:44 -08:00
parent f212909325
commit f85f8ebbfa
56 changed files with 11239 additions and 0 deletions

View File

@@ -77,5 +77,6 @@ include include/automake.mk
include third-party/automake.mk include third-party/automake.mk
include debian/automake.mk include debian/automake.mk
include vswitchd/automake.mk include vswitchd/automake.mk
include ovsdb/automake.mk
include xenserver/automake.mk include xenserver/automake.mk
include extras/ezio/automake.mk include extras/ezio/automake.mk

View File

@@ -77,6 +77,15 @@ lib_libopenvswitch_a_SOURCES = \
lib/ofp-print.h \ lib/ofp-print.h \
lib/ofpbuf.c \ lib/ofpbuf.c \
lib/ofpbuf.h \ lib/ofpbuf.h \
lib/ovsdb-client.h \
lib/ovsdb-data.c \
lib/ovsdb-data.h \
lib/ovsdb-error.c \
lib/ovsdb-error.h \
lib/ovsdb-parser.c \
lib/ovsdb-parser.h \
lib/ovsdb-types.c \
lib/ovsdb-types.h \
lib/packets.c \ lib/packets.c \
lib/packets.h \ lib/packets.h \
lib/pcap.c \ lib/pcap.c \
@@ -104,6 +113,8 @@ lib_libopenvswitch_a_SOURCES = \
lib/signals.h \ lib/signals.h \
lib/socket-util.c \ lib/socket-util.c \
lib/socket-util.h \ lib/socket-util.h \
lib/sort.c \
lib/sort.h \
lib/stp.c \ lib/stp.c \
lib/stp.h \ lib/stp.h \
lib/stream-fd.c \ lib/stream-fd.c \

View File

@@ -26,5 +26,6 @@
#define ALWAYS_INLINE __attribute__((always_inline)) #define ALWAYS_INLINE __attribute__((always_inline))
#define likely(x) __builtin_expect((x),1) #define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0) #define unlikely(x) __builtin_expect((x),0)
#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
#endif /* compiler.h */ #endif /* compiler.h */

756
lib/ovsdb-data.c Normal file
View File

@@ -0,0 +1,756 @@
/* Copyright (c) 2009 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 "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();
}
}
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 uuid *named_uuid;
ovsdb_error_destroy(error0);
named_uuid = ovsdb_symbol_table_get(symtab, name);
if (named_uuid) {
*uuid = *named_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);
}
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]);
}
}
static 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));
}
static bool
ovsdb_datum_contains(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 {
return (type->value_type == OVSDB_TYPE_VOID
|| ovsdb_atom_equals(&a->values[i], &b->values[j],
type->value_type));
}
}
return false;
}
/* 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_contains(a, i, b, type)) {
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_contains(a, i, b, type)) {
return false;
}
}
return true;
}
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) {
free(node->data);
shash_delete(&symtab->sh, node);
}
shash_destroy(&symtab->sh);
free(symtab);
}
}
const struct uuid *
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)
{
struct uuid *entry = shash_find_data(&symtab->sh, name);
if (!entry) {
shash_add(&symtab->sh, name, xmemdup(uuid, sizeof *uuid));
} else {
*entry = *uuid;
}
}

128
lib/ovsdb-data.h Normal file
View File

@@ -0,0 +1,128 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_DATA_H
#define OVSDB_DATA_H 1
#include <stdlib.h>
#include "compiler.h"
#include "ovsdb-types.h"
struct ovsdb_symbol_table;
/* One value of an atomic type (given by enum ovs_atomic_type). */
union ovsdb_atom {
int64_t integer;
double real;
bool boolean;
char *string;
struct uuid uuid;
};
void ovsdb_atom_init_default(union ovsdb_atom *, enum ovsdb_atomic_type);
void ovsdb_atom_clone(union ovsdb_atom *, const union ovsdb_atom *,
enum ovsdb_atomic_type);
void ovsdb_atom_swap(union ovsdb_atom *, union ovsdb_atom *);
static inline bool
ovsdb_atom_needs_destruction(enum ovsdb_atomic_type type)
{
return type == OVSDB_TYPE_STRING;
}
static inline void
ovsdb_atom_destroy(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
{
if (type == OVSDB_TYPE_STRING) {
free(atom->string);
}
}
uint32_t ovsdb_atom_hash(const union ovsdb_atom *, enum ovsdb_atomic_type,
uint32_t basis);
int ovsdb_atom_compare_3way(const union ovsdb_atom *,
const union ovsdb_atom *,
enum ovsdb_atomic_type);
static inline bool ovsdb_atom_equals(const union ovsdb_atom *a,
const union ovsdb_atom *b,
enum ovsdb_atomic_type type)
{
return !ovsdb_atom_compare_3way(a, b, type);
}
struct ovsdb_error *ovsdb_atom_from_json(union ovsdb_atom *,
enum ovsdb_atomic_type,
const struct json *,
const struct ovsdb_symbol_table *)
WARN_UNUSED_RESULT;
struct json *ovsdb_atom_to_json(const union ovsdb_atom *,
enum ovsdb_atomic_type);
/* One value of an OVSDB type (given by struct ovsdb_type). */
struct ovsdb_datum {
unsigned int n; /* Number of 'keys' and 'values'. */
union ovsdb_atom *keys; /* Each of the ovsdb_type's 'key_type'. */
union ovsdb_atom *values; /* Each of the ovsdb_type's 'value_type'. */
};
void ovsdb_datum_init_default(struct ovsdb_datum *, const struct ovsdb_type *);
void ovsdb_datum_clone(struct ovsdb_datum *, const struct ovsdb_datum *,
const struct ovsdb_type *);
void ovsdb_datum_destroy(struct ovsdb_datum *, const struct ovsdb_type *);
void ovsdb_datum_swap(struct ovsdb_datum *, struct ovsdb_datum *);
struct ovsdb_error *ovsdb_datum_from_json(struct ovsdb_datum *,
const struct ovsdb_type *,
const struct json *,
const struct ovsdb_symbol_table *)
WARN_UNUSED_RESULT;
struct json *ovsdb_datum_to_json(const struct ovsdb_datum *,
const struct ovsdb_type *);
uint32_t ovsdb_datum_hash(const struct ovsdb_datum *,
const struct ovsdb_type *, uint32_t basis);
int ovsdb_datum_compare_3way(const struct ovsdb_datum *,
const struct ovsdb_datum *,
const struct ovsdb_type *);
bool ovsdb_datum_equals(const struct ovsdb_datum *,
const struct ovsdb_datum *,
const struct ovsdb_type *);
bool ovsdb_datum_includes_all(const struct ovsdb_datum *,
const struct ovsdb_datum *,
const struct ovsdb_type *);
bool ovsdb_datum_excludes_all(const struct ovsdb_datum *,
const struct ovsdb_datum *,
const struct ovsdb_type *);
static inline bool
ovsdb_datum_conforms_to_type(const struct ovsdb_datum *datum,
const struct ovsdb_type *type)
{
return datum->n >= type->n_min && datum->n <= type->n_max;
}
/* A table mapping from names to data items. Currently the data items are
* always UUIDs; perhaps this will be expanded in the future. */
struct ovsdb_symbol_table *ovsdb_symbol_table_create(void);
void ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *);
const struct uuid *ovsdb_symbol_table_get(const struct ovsdb_symbol_table *,
const char *name);
void ovsdb_symbol_table_put(struct ovsdb_symbol_table *, const char *name,
const struct uuid *);
#endif /* ovsdb-data.h */

221
lib/ovsdb-error.c Normal file
View File

@@ -0,0 +1,221 @@
/* Copyright (c) 2009 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-error.h"
#include <inttypes.h>
#include "backtrace.h"
#include "dynamic-string.h"
#include "json.h"
#include "util.h"
struct ovsdb_error {
const char *tag; /* String for "error" member. */
char *details; /* String for "details" member. */
char *syntax; /* String for "syntax" member. */
int errno_; /* Unix errno value, 0 if none. */
};
static struct ovsdb_error *
ovsdb_error_valist(const char *tag, const char *details, va_list args)
{
struct ovsdb_error *error = xmalloc(sizeof *error);
error->tag = tag ? tag : "ovsdb error";
error->details = details ? xvasprintf(details, args) : NULL;
error->syntax = NULL;
error->errno_ = 0;
return error;
}
struct ovsdb_error *
ovsdb_error(const char *tag, const char *details, ...)
{
struct ovsdb_error *error;
va_list args;
va_start(args, details);
error = ovsdb_error_valist(tag, details, args);
va_end(args);
return error;
}
struct ovsdb_error *
ovsdb_io_error(int errno_, const char *details, ...)
{
struct ovsdb_error *error;
va_list args;
va_start(args, details);
error = ovsdb_error_valist("I/O error", details, args);
va_end(args);
error->errno_ = errno_;
return error;
}
struct ovsdb_error *
ovsdb_syntax_error(const struct json *json, const char *tag,
const char *details, ...)
{
struct ovsdb_error *error;
va_list args;
va_start(args, details);
error = ovsdb_error_valist(tag ? tag : "syntax error", details, args);
va_end(args);
if (json) {
/* XXX this is much too much information in some cases */
error->syntax = json_to_string(json, 0);
}
return error;
}
struct ovsdb_error *
ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...)
{
va_list args;
char *msg;
va_start(args, details);
msg = xvasprintf(details, args);
va_end(args);
if (error->details) {
char *new = xasprintf("%s: %s", msg, error->details);
free(error->details);
error->details = new;
free(msg);
} else {
error->details = msg;
}
return error;
}
struct ovsdb_error *
ovsdb_internal_error(const char *file, int line, const char *details, ...)
{
struct ds ds = DS_EMPTY_INITIALIZER;
struct backtrace backtrace;
struct ovsdb_error *error;
va_list args;
ds_put_format(&ds, "%s:%d:", file, line);
if (details) {
ds_put_char(&ds, ' ');
va_start(args, details);
ds_put_format_valist(&ds, details, args);
va_end(args);
}
backtrace_capture(&backtrace);
if (backtrace.n_frames) {
int i;
ds_put_cstr(&ds, " (backtrace:");
for (i = 0; i < backtrace.n_frames; i++) {
ds_put_format(&ds, " 0x%08"PRIxPTR, backtrace.frames[i]);
}
ds_put_char(&ds, ')');
}
ds_put_format(&ds, " (%s %s%s)", program_name, VERSION, BUILDNR);
error = ovsdb_error("internal error", "%s", ds_cstr(&ds));
ds_destroy(&ds);
return error;
}
void
ovsdb_error_destroy(struct ovsdb_error *error)
{
if (error) {
free(error->details);
free(error->syntax);
free(error);
}
}
struct ovsdb_error *
ovsdb_error_clone(const struct ovsdb_error *old)
{
if (old) {
struct ovsdb_error *new = xmalloc(sizeof *new);
new->tag = old->tag;
new->details = old->details ? xstrdup(old->details) : NULL;
new->syntax = old->syntax ? xstrdup(old->syntax) : NULL;
new->errno_ = old->errno_;
return new;
} else {
return NULL;
}
}
static const char *
ovsdb_errno_string(int error)
{
return error == EOF ? "unexpected end of file" : strerror(error);
}
struct json *
ovsdb_error_to_json(const struct ovsdb_error *error)
{
struct json *json = json_object_create();
json_object_put_string(json, "error", error->tag);
if (error->details) {
json_object_put_string(json, "details", error->details);
}
if (error->syntax) {
json_object_put_string(json, "syntax", error->syntax);
}
if (error->errno_) {
json_object_put_string(json, "io-error",
ovsdb_errno_string(error->errno_));
}
return json;
}
char *
ovsdb_error_to_string(const struct ovsdb_error *error)
{
struct ds ds = DS_EMPTY_INITIALIZER;
if (error->syntax) {
ds_put_format(&ds, "syntax \"%s\": ", error->syntax);
}
ds_put_cstr(&ds, error->tag);
if (error->details) {
ds_put_format(&ds, ": %s", error->details);
}
if (error->errno_) {
ds_put_format(&ds, " (%s)", ovsdb_errno_string(error->errno_));
}
return ds_steal_cstr(&ds);
}
const char *
ovsdb_error_get_tag(const struct ovsdb_error *error)
{
return error->tag;
}

53
lib/ovsdb-error.h Normal file
View File

@@ -0,0 +1,53 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_ERROR_H
#define OVSDB_ERROR_H 1
#include "compiler.h"
struct json;
struct ovsdb_error *ovsdb_error(const char *tag, const char *details, ...)
PRINTF_FORMAT(2, 3)
WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_io_error(int error, const char *details, ...)
PRINTF_FORMAT(2, 3)
WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_syntax_error(const struct json *, const char *tag,
const char *details, ...)
PRINTF_FORMAT(3, 4)
WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_wrap_error(struct ovsdb_error *error,
const char *details, ...)
PRINTF_FORMAT(2, 3);
struct ovsdb_error *ovsdb_internal_error(const char *file, int line,
const char *details, ...)
PRINTF_FORMAT(3, 4)
WARN_UNUSED_RESULT;
#define OVSDB_BUG(MSG) ovsdb_internal_error(__FILE__, __LINE__, "%s", MSG)
void ovsdb_error_destroy(struct ovsdb_error *);
struct ovsdb_error *ovsdb_error_clone(const struct ovsdb_error *)
WARN_UNUSED_RESULT;
char *ovsdb_error_to_string(const struct ovsdb_error *);
struct json *ovsdb_error_to_json(const struct ovsdb_error *);
const char *ovsdb_error_get_tag(const struct ovsdb_error *);
#endif /* ovsdb-error.h */

167
lib/ovsdb-parser.c Normal file
View File

@@ -0,0 +1,167 @@
/* Copyright (c) 2009 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-parser.h"
#include <ctype.h>
#include <stdarg.h>
#include "ovsdb-error.h"
void
ovsdb_parser_init(struct ovsdb_parser *parser, const struct json *json,
const char *name, ...)
{
va_list args;
va_start(args, name);
parser->name = xvasprintf(name, args);
va_end(args);
svec_init(&parser->used);
parser->error = NULL;
parser->json = (json && json->type == JSON_OBJECT ? json : NULL);
if (!parser->json) {
ovsdb_parser_raise_error(parser, "Object expected.");
}
}
static bool
is_id(const char *string)
{
unsigned char c;
c = *string;
if (!isalpha(c) && c != '_') {
return false;
}
for (;;) {
c = *++string;
if (c == '\0') {
return true;
} else if (!isalpha(c) && !isdigit(c) && c != '_') {
return false;
}
}
}
const struct json *
ovsdb_parser_member(struct ovsdb_parser *parser, const char *name,
enum ovsdb_parser_types types)
{
struct json *value;
if (!parser->json) {
return NULL;
}
value = shash_find_data(json_object(parser->json), name);
if (!value) {
if (!(types & OP_OPTIONAL)) {
ovsdb_parser_raise_error(parser,
"Required '%s' member is missing.", name);
}
return NULL;
}
if (value->type >= 0 && value->type < JSON_N_TYPES
&& (types & (1u << value->type)
|| (types & OP_ID
&& value->type == JSON_STRING
&& is_id(value->u.string))))
{
svec_add(&parser->used, name);
return value;
} else {
ovsdb_parser_raise_error(parser, "Type mismatch for member '%s'.",
name);
return NULL;
}
}
void
ovsdb_parser_raise_error(struct ovsdb_parser *parser, const char *format, ...)
{
if (!parser->error) {
struct ovsdb_error *error;
va_list args;
char *message;
va_start(args, format);
message = xvasprintf(format, args);
va_end(args);
error = ovsdb_syntax_error(parser->json, NULL, "Parsing %s failed: %s",
parser->name, message);
free(message);
parser->error = error;
}
}
struct ovsdb_error *
ovsdb_parser_get_error(const struct ovsdb_parser *parser)
{
return parser->error ? ovsdb_error_clone(parser->error) : NULL;
}
bool
ovsdb_parser_has_error(const struct ovsdb_parser *parser)
{
return parser->error != NULL;
}
struct ovsdb_error *
ovsdb_parser_finish(struct ovsdb_parser *parser)
{
if (!parser->error) {
const struct shash *object = json_object(parser->json);
size_t n_unused;
/* XXX this method of detecting unused members can be made cheaper */
svec_sort_unique(&parser->used);
n_unused = shash_count(object) - parser->used.n;
if (n_unused) {
struct shash_node *node;
SHASH_FOR_EACH (node, object) {
if (!svec_contains(&parser->used, node->name)) {
if (n_unused > 1) {
ovsdb_parser_raise_error(
parser,
"Member '%s' and %zu other member%s "
"are present but not allowed here.",
node->name, n_unused - 1, n_unused > 2 ? "s" : "");
} else {
ovsdb_parser_raise_error(
parser,
"Member '%s' is present but not allowed here.",
node->name);
}
break;
}
}
}
}
free(parser->name);
svec_destroy(&parser->used);
return parser->error;
}

74
lib/ovsdb-parser.h Normal file
View File

@@ -0,0 +1,74 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_PARSER_H
#define OVSDB_PARSER_H 1
#include <stdbool.h>
#include "compiler.h"
#include "json.h"
#include "svec.h"
#include "util.h"
struct ovsdb_parser {
char *name; /* Used only in error messages. */
struct svec used; /* Already-parsed names from 'object'. */
const struct json *json; /* JSON object being parsed. */
struct ovsdb_error *error; /* Error signaled, if any. */
};
/* Check that the JSON types make the bitwise tricks below work OK. */
BUILD_ASSERT_DECL(JSON_NULL >= 0 && JSON_NULL < 10);
BUILD_ASSERT_DECL(JSON_FALSE >= 0 && JSON_FALSE < 10);
BUILD_ASSERT_DECL(JSON_TRUE >= 0 && JSON_TRUE < 10);
BUILD_ASSERT_DECL(JSON_OBJECT >= 0 && JSON_OBJECT < 10);
BUILD_ASSERT_DECL(JSON_ARRAY >= 0 && JSON_ARRAY < 10);
BUILD_ASSERT_DECL(JSON_INTEGER >= 0 && JSON_INTEGER < 10);
BUILD_ASSERT_DECL(JSON_REAL >= 0 && JSON_REAL < 10);
BUILD_ASSERT_DECL(JSON_STRING >= 0 && JSON_STRING < 10);
BUILD_ASSERT_DECL(JSON_N_TYPES == 8);
enum ovsdb_parser_types {
OP_NULL = 1 << JSON_NULL, /* null */
OP_FALSE = 1 << JSON_FALSE, /* false */
OP_TRUE = 1 << JSON_TRUE, /* true */
OP_OBJECT = 1 << JSON_OBJECT, /* {"a": b, "c": d, ...} */
OP_ARRAY = 1 << JSON_ARRAY, /* [1, 2, 3, ...] */
OP_INTEGER = 1 << JSON_INTEGER, /* 123. */
OP_NONINTEGER = 1 << JSON_REAL, /* 123.456. */
OP_STRING = 1 << JSON_STRING, /* "..." */
OP_BOOLEAN = OP_FALSE | OP_TRUE,
OP_NUMBER = OP_INTEGER | OP_NONINTEGER,
OP_ID = 1 << JSON_N_TYPES, /* "[_a-zA-Z][_a-zA-Z0-9]*" */
OP_OPTIONAL = 1 << (JSON_N_TYPES + 1) /* no value at all */
};
void ovsdb_parser_init(struct ovsdb_parser *, const struct json *,
const char *name, ...)
PRINTF_FORMAT(3, 4);
const struct json *ovsdb_parser_member(struct ovsdb_parser *, const char *name,
enum ovsdb_parser_types);
void ovsdb_parser_raise_error(struct ovsdb_parser *parser,
const char *format, ...)
PRINTF_FORMAT(2, 3);
bool ovsdb_parser_has_error(const struct ovsdb_parser *);
struct ovsdb_error *ovsdb_parser_get_error(const struct ovsdb_parser *);
struct ovsdb_error *ovsdb_parser_finish(struct ovsdb_parser *)
WARN_UNUSED_RESULT;
#endif /* ovsdb-parser.h */

251
lib/ovsdb-types.c Normal file
View File

@@ -0,0 +1,251 @@
/* Copyright (c) 2009 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-types.h"
#include <limits.h>
#include "dynamic-string.h"
#include "json.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
const struct ovsdb_type ovsdb_type_integer =
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_INTEGER);
const struct ovsdb_type ovsdb_type_real =
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_REAL);
const struct ovsdb_type ovsdb_type_boolean =
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_BOOLEAN);
const struct ovsdb_type ovsdb_type_string =
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_STRING);
const struct ovsdb_type ovsdb_type_uuid =
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_TYPE_UUID);
const char *
ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type)
{
switch (type) {
case OVSDB_TYPE_VOID:
return "void";
case OVSDB_TYPE_INTEGER:
return "integer";
case OVSDB_TYPE_REAL:
return "real";
case OVSDB_TYPE_BOOLEAN:
return "boolean";
case OVSDB_TYPE_STRING:
return "string";
case OVSDB_TYPE_UUID:
return "uuid";
case OVSDB_N_TYPES:
default:
return "<invalid>";
}
}
struct json *
ovsdb_atomic_type_to_json(enum ovsdb_atomic_type type)
{
return json_string_create(ovsdb_atomic_type_to_string(type));
}
bool
ovsdb_type_is_valid(const struct ovsdb_type *type)
{
return (type->key_type != OVSDB_TYPE_VOID
&& ovsdb_atomic_type_is_valid(type->key_type)
&& ovsdb_atomic_type_is_valid(type->value_type)
&& type->n_min <= type->n_max
&& (type->value_type == OVSDB_TYPE_VOID
|| ovsdb_atomic_type_is_valid_key(type->key_type)));
}
bool
ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type)
{
if (!strcmp(string, "integer")) {
*type = OVSDB_TYPE_INTEGER;
} else if (!strcmp(string, "real")) {
*type = OVSDB_TYPE_REAL;
} else if (!strcmp(string, "boolean")) {
*type = OVSDB_TYPE_BOOLEAN;
} else if (!strcmp(string, "string")) {
*type = OVSDB_TYPE_STRING;
} else if (!strcmp(string, "uuid")) {
*type = OVSDB_TYPE_UUID;
} else {
return false;
}
return true;
}
struct ovsdb_error *
ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type,
const struct json *json)
{
if (json->type == JSON_STRING) {
if (ovsdb_atomic_type_from_string(json_string(json), type)) {
return NULL;
} else {
*type = OVSDB_TYPE_VOID;
return ovsdb_syntax_error(json, NULL,
"\"%s\" is not an atomic-type",
json_string(json));
}
} else {
*type = OVSDB_TYPE_VOID;
return ovsdb_syntax_error(json, NULL, "atomic-type expected");
}
}
static struct ovsdb_error *
n_from_json(const struct json *json, unsigned int *n)
{
if (!json) {
return NULL;
} else if (json->type == JSON_INTEGER
&& json->u.integer >= 0 && json->u.integer < UINT_MAX) {
*n = json->u.integer;
return NULL;
} else {
return ovsdb_syntax_error(json, NULL, "bad min or max value");
}
}
char *
ovsdb_type_to_english(const struct ovsdb_type *type)
{
const char *key = ovsdb_atomic_type_to_string(type->key_type);
const char *value = ovsdb_atomic_type_to_string(type->value_type);
if (ovsdb_type_is_scalar(type)) {
return xstrdup(key);
} else {
struct ds s = DS_EMPTY_INITIALIZER;
ds_put_cstr(&s, ovsdb_type_is_set(type) ? "set" : "map");
if (type->n_max == UINT_MAX) {
if (type->n_min) {
ds_put_format(&s, " of %u or more", type->n_min);
} else {
ds_put_cstr(&s, " of");
}
} else if (type->n_min) {
ds_put_format(&s, " of %u to %u", type->n_min, type->n_max);
} else {
ds_put_format(&s, " of up to %u", type->n_max);
}
if (ovsdb_type_is_set(type)) {
ds_put_format(&s, " %ss", key);
} else {
ds_put_format(&s, " (%s, %s) pairs", key, value);
}
return ds_cstr(&s);
}
}
struct ovsdb_error *
ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json)
{
type->value_type = OVSDB_TYPE_VOID;
type->n_min = 1;
type->n_max = 1;
if (json->type == JSON_STRING) {
return ovsdb_atomic_type_from_json(&type->key_type, json);
} else if (json->type == JSON_OBJECT) {
const struct json *key, *value, *min, *max;
struct ovsdb_error *error;
struct ovsdb_parser parser;
ovsdb_parser_init(&parser, json, "ovsdb type");
key = ovsdb_parser_member(&parser, "key", OP_STRING);
value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OPTIONAL);
min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL);
max = ovsdb_parser_member(&parser, "max",
OP_INTEGER | OP_STRING | OP_OPTIONAL);
error = ovsdb_parser_finish(&parser);
if (error) {
return error;
}
error = ovsdb_atomic_type_from_json(&type->key_type, key);
if (error) {
return error;
}
if (value) {
error = ovsdb_atomic_type_from_json(&type->value_type, value);
if (error) {
return error;
}
}
error = n_from_json(min, &type->n_min);
if (error) {
return error;
}
if (max && max->type == JSON_STRING
&& !strcmp(max->u.string, "unlimited")) {
type->n_max = UINT_MAX;
} else {
error = n_from_json(max, &type->n_max);
if (error) {
return error;
}
}
if (!ovsdb_type_is_valid(type)) {
return ovsdb_syntax_error(json, NULL,
"ovsdb type fails constraint checks");
}
return NULL;
} else {
return ovsdb_syntax_error(json, NULL, "ovsdb type expected");
}
}
struct json *
ovsdb_type_to_json(const struct ovsdb_type *type)
{
if (ovsdb_type_is_scalar(type)) {
return ovsdb_atomic_type_to_json(type->key_type);
} else {
struct json *json = json_object_create();
json_object_put(json, "key",
ovsdb_atomic_type_to_json(type->key_type));
if (type->value_type != OVSDB_TYPE_VOID) {
json_object_put(json, "value",
ovsdb_atomic_type_to_json(type->value_type));
}
if (type->n_min != 1) {
json_object_put(json, "min", json_integer_create(type->n_min));
}
if (type->n_max == UINT_MAX) {
json_object_put_string(json, "max", "unlimited");
} else if (type->n_max != 1) {
json_object_put(json, "max", json_integer_create(type->n_max));
}
return json;
}
}

126
lib/ovsdb-types.h Normal file
View File

@@ -0,0 +1,126 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_TYPES_H
#define OVSDB_TYPES_H 1
#include <stdbool.h>
#include <stdint.h>
#include "compiler.h"
#include "uuid.h"
struct json;
/* An atomic type: one that OVSDB regards as a single unit of data. */
enum ovsdb_atomic_type {
OVSDB_TYPE_VOID, /* No value. */
OVSDB_TYPE_INTEGER, /* Signed 64-bit integer. */
OVSDB_TYPE_REAL, /* IEEE 754 double-precision floating point. */
OVSDB_TYPE_BOOLEAN, /* True or false. */
OVSDB_TYPE_STRING, /* UTF-8 string. */
OVSDB_TYPE_UUID, /* RFC 4122 UUID referencing a table row. */
OVSDB_N_TYPES
};
static inline bool ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type);
static inline bool ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type);
bool ovsdb_atomic_type_from_string(const char *, enum ovsdb_atomic_type *);
struct ovsdb_error *ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *,
const struct json *);
const char *ovsdb_atomic_type_to_string(enum ovsdb_atomic_type);
struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
/* An OVSDB type. One of:
*
* - An atomic type.
*
* - A set of atomic types.
*
* - A map from one atomic type to another.
*/
struct ovsdb_type {
enum ovsdb_atomic_type key_type;
enum ovsdb_atomic_type value_type;
unsigned int n_min;
unsigned int n_max; /* UINT_MAX stands in for "unlimited". */
};
#define OVSDB_TYPE_SCALAR_INITIALIZER(KEY_TYPE) \
{ KEY_TYPE, OVSDB_TYPE_VOID, 1, 1 }
extern const struct ovsdb_type ovsdb_type_integer;
extern const struct ovsdb_type ovsdb_type_real;
extern const struct ovsdb_type ovsdb_type_boolean;
extern const struct ovsdb_type ovsdb_type_string;
extern const struct ovsdb_type ovsdb_type_uuid;
bool ovsdb_type_is_valid(const struct ovsdb_type *);
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *);
static inline bool ovsdb_type_is_optional(const struct ovsdb_type *);
static inline bool ovsdb_type_is_composite(const struct ovsdb_type *);
static inline bool ovsdb_type_is_set(const struct ovsdb_type *);
static inline bool ovsdb_type_is_map(const struct ovsdb_type *);
char *ovsdb_type_to_english(const struct ovsdb_type *);
struct ovsdb_error *ovsdb_type_from_json(struct ovsdb_type *,
const struct json *)
WARN_UNUSED_RESULT;
struct json *ovsdb_type_to_json(const struct ovsdb_type *);
/* Inline function implementations. */
static inline bool
ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type atomic_type)
{
return atomic_type >= 0 && atomic_type < OVSDB_N_TYPES;
}
static inline bool
ovsdb_atomic_type_is_valid_key(enum ovsdb_atomic_type atomic_type)
{
/* XXX should we disallow reals or booleans as keys? */
return ovsdb_atomic_type_is_valid(atomic_type);
}
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type)
{
return (type->value_type == OVSDB_TYPE_VOID
&& type->n_min == 1 && type->n_max == 1);
}
static inline bool ovsdb_type_is_optional(const struct ovsdb_type *type)
{
return type->n_min == 0;
}
static inline bool ovsdb_type_is_composite(const struct ovsdb_type *type)
{
return type->n_max > 1;
}
static inline bool ovsdb_type_is_set(const struct ovsdb_type *type)
{
return (type->value_type == OVSDB_TYPE_VOID
&& (type->n_min != 1 || type->n_max != 1));
}
static inline bool ovsdb_type_is_map(const struct ovsdb_type *type)
{
return type->value_type != OVSDB_TYPE_VOID;
}
#endif /* ovsdb-types.h */

70
lib/sort.c Normal file
View File

@@ -0,0 +1,70 @@
/* Copyright (c) 2009 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 "sort.h"
#include "random.h"
static size_t
partition(size_t p, size_t r,
int (*compare)(size_t a, size_t b, void *aux),
void (*swap)(size_t a, size_t b, void *aux),
void *aux)
{
size_t x = r - 1;
size_t i, j;
i = p;
for (j = p; j < x; j++) {
if (compare(j, x, aux) <= 0) {
swap(i++, j, aux);
}
}
swap(i, x, aux);
return i;
}
static void
quicksort(size_t p, size_t r,
int (*compare)(size_t a, size_t b, void *aux),
void (*swap)(size_t a, size_t b, void *aux),
void *aux)
{
size_t i, q;
if (r - p < 2) {
return;
}
i = random_range(r - p) + p;
if (r - 1 != i) {
swap(r - 1, i, aux);
}
q = partition(p, r, compare, swap, aux);
quicksort(p, q, compare, swap, aux);
quicksort(q, r, compare, swap, aux);
}
void
sort(size_t count,
int (*compare)(size_t a, size_t b, void *aux),
void (*swap)(size_t a, size_t b, void *aux),
void *aux)
{
quicksort(0, count, compare, swap, aux);
}

26
lib/sort.h Normal file
View File

@@ -0,0 +1,26 @@
/* Copyright (c) 2009 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.
*/
#ifndef SORT_H
#define SORT_H 1
#include <stddef.h>
void sort(size_t count,
int (*compare)(size_t a, size_t b, void *aux),
void (*swap)(size_t a, size_t b, void *aux),
void *aux);
#endif /* sort.h */

View File

@@ -54,6 +54,11 @@ VLOG_MODULE(ofctl)
VLOG_MODULE(ovs_discover) VLOG_MODULE(ovs_discover)
VLOG_MODULE(ofproto) VLOG_MODULE(ofproto)
VLOG_MODULE(openflowd) VLOG_MODULE(openflowd)
VLOG_MODULE(ovsdb)
VLOG_MODULE(ovsdb_file)
VLOG_MODULE(ovsdb_jsonrpc_server)
VLOG_MODULE(ovsdb_server)
VLOG_MODULE(ovsdb_tool)
VLOG_MODULE(pktbuf) VLOG_MODULE(pktbuf)
VLOG_MODULE(pcap) VLOG_MODULE(pcap)
VLOG_MODULE(poll_loop) VLOG_MODULE(poll_loop)

628
ovsdb/SPECS Normal file
View File

@@ -0,0 +1,628 @@
===================================================
Open vSwitch Configuration Database Specification
===================================================
Basic Notation
--------------
The descriptions below use the following shorthand notations for JSON
values. Additional notation is presented later.
<string>
A JSON string.
<id>
A JSON string matching [a-zA-Z_][a-zA-Z0-9_]*.
<id>s that begin with _ are reserved to the implementation and may
not be used by the user.
<boolean>
A JSON true or false value.
<number>
A JSON number.
<integer>
A JSON number with an integer value, within a certain range
(currently -2**63...+2**63-1).
Schema Format
-------------
An Open vSwitch configuration database consists of a set of tables,
each of which has a number of columns and zero or more rows. A schema
is represented by <database-schema>, as described below.
<database-schema>
A JSON object with the following members:
"name": <id> required
"comment": <string> optional
"tables": {<id>: <table-schema>, ...} required
The "name" identifies the database as a whole. The "comment"
optionally provides more information about the database. The
value of "tables" is a JSON object whose names are table names and
whose values are <table-schema>s.
<table-schema>
A JSON object with the following members:
"comment": <string> optional
"columns": {<id>: <column-schema>, ...} required
The "comment" optionally provides information about this table for
a human reader. The value of "tables" is a JSON object whose
names are table names and whose values are <column-schema>s.
Every table has the following columns whose definitions are not
included in the schema:
"_uuid": This column, which contains exactly one UUID value,
is initialized to a random value by the database engine when
it creates a row. It is read-only, and its value never
changes during the lifetime of a row.
"_version": Like "_uuid", this column contains exactly one
UUID value, initialized to a random value by the database
engine when it creates a row, and it is read-only. However,
its value changes to a new random value whenever any other
field in the row changes. Furthermore, its value is
ephemeral: when the database is closed and reopened, or when
the database process is stopped and then started again, each
"_version" also changes to a new random value.
<column-schema>
A JSON object with the following members:
"comment": <string> optional
"type": <type> required
"ephemeral": <boolean> optional
The "comment" optionally provides information about this column
for a human reader. The "type" specifies the type of data stored
in this column. If "ephemeral" is specified as true, then this
column's values are not guaranteed to be durable; they may be lost
when the database restarts.
<type>
The type of a database column. Either an <atomic-type> or a JSON
object that describes the type of a database column, with the
following members:
"key": <atomic-type> required
"value": <atomic-type> optional
"min": <integer> optional
"max": <integer> or "unlimited" optional
If "min" or "max" is not specified, each defaults to 1. If "max"
is specified as "unlimited", then there is no specified maximum
number of elements, although the implementation will enforce some
limit. After considering defaults, "min" must be at least 0,
"max" must be at least 1, and "max" must be greater than or equal
to "min".
If "min" and "max" are both 1 and "value" is not specified, the
type is the scalar type specified by "key".
If "min" is not 1 or "max" is not 1, or both, and "value" is not
specified, the type is a set of scalar type "key".
If "value" is specified, the type is a map from type "key" to type
"value".
<atomic-type>
One of the strings "integer", "real", "boolean", "string", or
"uuid", representing the specified scalar type.
Wire Protocol
-------------
The database wire protocol is implemented in JSON-RPC 1.0. It
consists of the following JSON-RPC methods:
get_schema
..........
Request object members:
"method": "get_schema" required
"params": [] required
"id": any JSON value except null required
Response object members:
"result": <database-schema>
"error": null
"id": same "id" as request
This operation retrieves a <database-schema> that describes the
hosted database.
transact
........
Request object members:
"method": "transact" required
"params": [<operation>*] required
"id": any JSON value except null required
Response object members:
"result": [<object>*]
"error": null
"id": same "id" as request
The "params" array for this method consists of zero or more JSON
objects, each of which represents a single database operation. The
"Operations" section below describes the valid operations.
The value of "id" must be unique among all in-flight transactions
within the current JSON-RPC session. Otherwise, the server may return
a JSON-RPC error.
The database server executes each of the specified operations in the
specified order, except that if an operation fails, then the remaining
operations are not executed.
The set of operations is executed as a single atomic, consistent,
isolated transaction. The transaction is committed only if every
operation succeeds. Durability of the commit is not guaranteed unless
the "commit" operation, with "durable" set to true, is included in the
operation set (see below).
Regardless of whether errors occur, the response is always a JSON-RPC
response with null "error" and a "result" member that is an array with
the same number of elements as "params". Each element of the "result"
array corresponds to the same element of the "params" array. The
"result" array elements may be interpreted as follows:
- A JSON object that does not contain an "error" member indicates
that the operation completed successfully. The specific members
of the object are specified below in the descriptions of
individual operations. Some operations do not produce any
results, in which case the object will have no members.
- A JSON object that contains a "error" member indicates that the
operation completed with an error. The value of the "error"
member is a short string, specified in this document, that
broadly indicates the class of the error. Besides the ones
listed for a specific operation, any operation may result in one
the following "error"s:
"error": "resources exhausted"
The operation or the transaction requires more resources
(memory, disk, CPU, etc.) than are currently available to
the database server.
"error": "syntax error"
The operation is not specified correctly: a required request
object member is missing, an unknown or unsupported request
object member is present, the operation attempts to act on a
table that does not exist, the operation modifies a
read-only table column, etc.
Database implementations may use "error" strings not specified
in this document to indicate errors that do not fit into any of
the specified categories.
Optionally, the object may include a "details" member, whose
value is a string that describes the error in more detail for
the benefit of a human user or administrator. The object may
also have other members that describe the error in more detail.
This document does not specify the names or values of these
members.
- A JSON null value indicates that the operation was not attempted
because a prior operation failed.
In general, "result" contains some number of successful results,
possibly followed by an error, in turn followed by enough JSON null
values to match the number of elements in "params". There is one
exception: if all of the operations succeed, but the results cannot be
committed (e.g. due to I/O errors), then "result" will have one more
element than "params", with the additional element describing the
error.
If "params" contains one or more "wait" operations, then the
transaction may take an arbitrary amount of time to complete. The
database implementation must be capable of accepting, executing, and
replying to other transactions and other JSON-RPC requests while a
transaction or transactions containing "wait" operations are
outstanding on the same or different JSON-RPC sessions.
The section "Notation for the Wire Protocol" below describes
additional notation for use with the wire protocol. After that, the
"Operations" section describes each operation.
cancel
......
Request object members:
"method": "cancel" required
"params": the "id" for an outstanding request required
"id": null required
Response object members:
<no response>
This JSON-RPC notification instructs the database server to
immediately complete or cancel the "transact" request whose "id" is
the same as the notification's "params" value.
If the "transact" request can be completed immediately, then the
server sends a response in the form described for "transact", above.
Otherwise, the server sends a JSON-RPC error response of the following
form:
"result": null
"error": "canceled"
"id": the request "id" member
The "cancel" notification itself has no reply.
Notation for the Wire Protocol
------------------------------
<table>
An <id> that names a table.
<column>
An <id> that names a table column.
<row>
A JSON object that describes a table row or a subset of a table
row. Each member is the name of a table column paired with the
<value> of that column.
<value>
A JSON value that represents the value of a column in a table row,
one of <atom>, a <set>, or a <map>.
<atom>
A JSON value that represents a scalar value for a column, one of
<string>, <number>, <boolean>, <uuid>, <named-uuid>.
<set>
A 2-element JSON array that represents a database set value. The
first element of the array must be the string "set" and the second
element must be an array of zero or more <atom>s giving the values
in the set. All of the <atom>s must have the same type.
<map>
A 2-element JSON array that represents a database map value. The
first element of the array must be the string "map" and the second
element must be an array of zero or more <pair>s giving the values
in the map. All of the <pair>s must have the same key and value
types.
(JSON objects are not used to represent <map> because JSON only
allows string names in an object.)
<pair>
A 2-element JSON array that represents a pair within a database
map. The first element is an <atom> that represents the key, the
second element is an <atom> that represents the value.
<uuid>
A 2-element JSON array that represents a UUID. The first element
of the array must be the string "uuid" and the second element must
be a 36-character string giving the UUID in the format described
by RFC 4122. For example, the following <uuid> represents the
UUID 550e8400-e29b-41d4-a716-446655440000:
["uuid", "550e8400-e29b-41d4-a716-446655440000"]
<named-uuid>
A 2-element JSON array that represents the UUID of a row inserted
in a previous "insert" operation within the same transaction. The
first element of the array must be the string "named-uuid" and the
second element must be the string specified on a previous "insert"
operation's "uuid-name". For example, if a previous "insert"
operation specified a "uuid-name" of "myrow", the following
<named-uuid> represents the UUID created by that operation:
["named-uuid", "myrow"]
<condition>
A 3-element JSON array of the form [<column>, <function>,
<value>] that represents a test on a column value.
Except as otherwise specified below, <value> must have the same
type as <column>.
The meaning depends on the type of <column>:
integer
real
<function> must be "<", "<=", "==", "!=", ">=", ">",
"includes", or "excludes".
The test is true if the column's value satisfies the
relation <function> <value>, e.g. if the column has value
1 and <value> is 2, the test is true if <function> is "<",
"<=" or "!=", but not otherwise.
"includes" is equivalent to "=="; "excludes" is equivalent
to "!=".
boolean
string
uuid
<function> must be "!=", "==", "includes", or "excludes".
If <function> is "==" or "includes", the test is true if
the column's value equals <value>. If <function> is "!="
or "excludes", the test is inverted.
set
map
<function> must be "!=", "==", "includes", or "excludes".
If <function> is "==", the test is true if the column's
value contains exactly the same values (for sets) or pairs
(for maps). If <function> is "!=", the test is inverted.
If <function> is "includes", the test is true if the
column's value contains all of the values (for sets) or
pairs (for maps) in <value>. The column's value may also
contain other values or pairs.
If <function> is "excludes", the test is true if the
column's value does not contain any of the values (for
sets) or pairs (for maps) in <value>. The column's value
may contain other values or pairs not in <value>.
If <function> is "includes" or "excludes", then the
required type of <value> is slightly relaxed, in that it
may have fewer than the minimum number of elements
specified by the column's type. If <function> is
"excludes", then the required type is additionally relaxed
in that <value> may have more than the maximum number of
elements specified by the column's type.
<function>
One of "<", "<=", "==", "!=", ">=", ">", "includes", "excludes".
Operations
----------
Each of the available operations is described below.
insert
......
Request object members:
"op": "insert" required
"table": <table> required
"row": <row> required
"uuid-name": <string> optional
Result object members:
"uuid": <uuid>
Semantics:
Inserts "row" into "table". If "row" does not specify values
for all the columns in "table", those columns receive default
values.
The new row receives a new, randomly generated UUID, which is
returned as the "_uuid" member of the result. If "uuid-name"
is supplied, then the UUID is made available under that name
to later operations within the same transaction.
select
......
Request object members:
"op": "select" required
"table": <table> required
"where": [<condition>*] required
"columns": [<column>*] optional
Result object members:
"rows": [<row>*]
Semantics:
Searches "table" for rows that match all the conditions specified
in "where". If "where" is an empty array, every row in "table" is
selected.
The "rows" member of the result is an array of objects. Each
object corresponds to a matching row, with each column
specified in "columns" as a member, the column's name as the
member name and its value as the member value. If "columns"
is not specified, all the table's columns are included. If
two rows of the result have the same values for all included
columns, only one copy of that row is included in "rows".
Specifying "_uuid" within "columns" will avoid dropping
duplicates, since every row has a unique UUID.
The ordering of rows within "rows" is unspecified.
update
......
Request object members:
"op": "update" required
"table": <table> required
"where": [<condition>*] required
"row": <row> required
Result object members:
"count": <integer>
Semantics:
Updates rows in a table.
Searches "table" for rows that match all the conditions
specified in "where". For each matching row, changes the
value of each column specified in "row" to the value for that
column specified in "row".
The "_uuid" and "_version" columns of a table may not be updated.
Columns designated read-only in the schema also may not be
updated.
The "count" member of the result specifies the number of rows
that matched.
delete
......
Request object members:
"op": "delete" required
"table": <table> required
"where": [<condition>*] required
Result object members:
"count": <integer>
Semantics:
Deletes all the rows from "table" that match all the conditions
specified in "where".
The "count" member of the result specifies the number of deleted
rows.
wait
....
Request object members:
"op": "wait" required
"timeout": <integer> optional
"table": <table> required
"where": [<condition>*] required
"columns": [<column>*] required
"until": "==" or "!=" required
"rows": [<row>*] required
Result object members:
none
Semantics:
Waits until a condition becomes true.
If "until" is "==", checks whether the query on "table" specified
by "where" and "columns", which is evaluated in the same way as
specified for "select", returns the result set specified by
"rows". If it does, then the operation completes successfully.
Otherwise, the entire transaction rolls back. It is automatically
restarted later, after a change in the database makes it possible
for the operation to succeed. The client will not receive a
response until the operation permanently succeeds or fails.
If "until" is "!=", the sense of the test is negated. That is, as
long as the query on "table" specified by "where" and "columns"
returns "rows", the transaction will be rolled back and restarted
later.
If "timeout" is specified, then the transaction aborts after the
specified number of milliseconds. The transaction is guaranteed
to be attempted at least once before it aborts. A "timeout" of 0
will abort the transaction on the first mismatch.
Errors:
"error": "not supported"
One or more of the columns in this table do not support
triggers. This error will not occur if "timeout" is 0.
"error": "timed out"
The "timeout" was reached before the transaction was able to
complete.
commit
......
Request object members:
"op": "commit" required
"durable": <boolean> required
Result object members:
none
Semantics:
If "durable" is specified as true, then the transaction, if it
commits, will be stored durably (to disk) before the reply is sent
to the client.
Errors:
"error": "not supported"
When "durable" is true, this database implementation does not
support durable commits.
abort
.....
Request object members:
"op": "abort" required
Result object members:
(never succeeds)
Semantics:
Aborts the transaction with an error. This may be useful for
testing.
Errors:
"error": "aborted"
This operation always fails with this error.

44
ovsdb/automake.mk Normal file
View File

@@ -0,0 +1,44 @@
# libovsdb
noinst_LIBRARIES += ovsdb/libovsdb.a
ovsdb_libovsdb_a_SOURCES = \
ovsdb/column.c \
ovsdb/column.h \
ovsdb/condition.c \
ovsdb/condition.h \
ovsdb/execution.c \
ovsdb/file.c \
ovsdb/file.h \
ovsdb/jsonrpc-server.c \
ovsdb/jsonrpc-server.h \
ovsdb/ovsdb-server.c \
ovsdb/ovsdb.c \
ovsdb/ovsdb.h \
ovsdb/query.c \
ovsdb/query.h \
ovsdb/row.c \
ovsdb/row.h \
ovsdb/table.c \
ovsdb/table.h \
ovsdb/trigger.c \
ovsdb/trigger.h \
ovsdb/transaction.c \
ovsdb/transaction.h
# ovsdb-tool
bin_PROGRAMS += ovsdb/ovsdb-tool
ovsdb_ovsdb_tool_SOURCES = ovsdb/ovsdb-tool.c
ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
## ovsdb-tool.8
#man_MANS += ovsdb/ovsdb-tool.8
#DISTCLEANFILES += ovsdb/ovsdb-tool.8
#EXTRA_DIST += ovsdb/ovsdb-tool.8.in
# ovsdb-server
sbin_PROGRAMS += ovsdb/ovsdb-server
ovsdb_ovsdb_server_SOURCES = ovsdb/ovsdb-server.c
ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(FAULT_LIBS)
## ovsdb-server.8
#man_MANS += ovsdb/ovsdb-server.8
#DISTCLEANFILES += ovsdb/ovsdb-server.8
#EXTRA_DIST += ovsdb/ovsdb-server.8.in

232
ovsdb/column.c Normal file
View File

@@ -0,0 +1,232 @@
/* Copyright (c) 2009 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/column.h"
#include <stdlib.h>
#include "column.h"
#include "json.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "table.h"
#include "util.h"
struct ovsdb_column *
ovsdb_column_create(const char *name, const char *comment,
bool mutable, bool persistent,
const struct ovsdb_type *type)
{
struct ovsdb_column *ts;
ts = xzalloc(sizeof *ts);
ts->name = xstrdup(name);
ts->comment = comment ? xstrdup(comment) : NULL;
ts->mutable = mutable;
ts->persistent = persistent;
ts->type = *type;
return ts;
}
void
ovsdb_column_destroy(struct ovsdb_column *column)
{
free(column->name);
free(column->comment);
free(column);
}
struct ovsdb_error *
ovsdb_column_from_json(const struct json *json, const char *name,
struct ovsdb_column **columnp)
{
const struct json *comment, *mutable, *ephemeral, *type_json;
struct ovsdb_error *error;
struct ovsdb_type type;
struct ovsdb_parser parser;
bool persistent;
*columnp = NULL;
ovsdb_parser_init(&parser, json, "schema for column %s", name);
comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL);
mutable = ovsdb_parser_member(&parser, "mutable",
OP_TRUE | OP_FALSE | OP_OPTIONAL);
ephemeral = ovsdb_parser_member(&parser, "ephemeral",
OP_TRUE | OP_FALSE | OP_OPTIONAL);
type_json = ovsdb_parser_member(&parser, "type", OP_STRING | OP_OBJECT);
error = ovsdb_parser_finish(&parser);
if (error) {
return error;
}
error = ovsdb_type_from_json(&type, type_json);
if (error) {
return error;
}
persistent = ephemeral ? !json_boolean(ephemeral) : true;
*columnp = ovsdb_column_create(name,
comment ? json_string(comment) : NULL,
mutable ? json_boolean(mutable) : true,
persistent, &type);
return NULL;
}
struct json *
ovsdb_column_to_json(const struct ovsdb_column *column)
{
struct json *json = json_object_create();
if (column->comment) {
json_object_put_string(json, "comment", column->comment);
}
if (!column->mutable) {
json_object_put(json, "mutable", json_boolean_create(false));
}
if (!column->persistent) {
json_object_put(json, "ephemeral", json_boolean_create(true));
}
json_object_put(json, "type", ovsdb_type_to_json(&column->type));
return json;
}
void
ovsdb_column_set_init(struct ovsdb_column_set *set)
{
set->columns = NULL;
set->n_columns = set->allocated_columns = 0;
}
void
ovsdb_column_set_destroy(struct ovsdb_column_set *set)
{
free(set->columns);
}
void
ovsdb_column_set_clone(struct ovsdb_column_set *new,
const struct ovsdb_column_set *old)
{
new->columns = xmemdup(old->columns,
old->n_columns * sizeof *old->columns);
new->n_columns = new->allocated_columns = old->n_columns;
}
struct ovsdb_error *
ovsdb_column_set_from_json(const struct json *json,
const struct ovsdb_table *table,
struct ovsdb_column_set *set)
{
ovsdb_column_set_init(set);
if (!json) {
struct shash_node *node;
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_column_set_add(set, column);
}
return NULL;
} else {
size_t i;
if (json->type != JSON_ARRAY) {
goto error;
}
/* XXX this is O(n**2) */
for (i = 0; i < json->u.array.n; i++) {
struct ovsdb_column *column;
if (json->u.array.elems[i]->type != JSON_STRING) {
goto error;
}
column = shash_find_data(&table->schema->columns,
json->u.array.elems[i]->u.string);
if (ovsdb_column_set_contains(set, column->index)) {
goto error;
}
ovsdb_column_set_add(set, column);
}
return NULL;
}
error:
ovsdb_column_set_destroy(set);
return ovsdb_syntax_error(json, NULL,
"array of distinct column names expected");
}
void
ovsdb_column_set_add(struct ovsdb_column_set *set,
const struct ovsdb_column *column)
{
if (set->n_columns >= set->allocated_columns) {
set->columns = x2nrealloc(set->columns, &set->allocated_columns,
sizeof *set->columns);
}
set->columns[set->n_columns++] = column;
}
void
ovsdb_column_set_add_all(struct ovsdb_column_set *set,
const struct ovsdb_table *table)
{
struct shash_node *node;
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_column_set_add(set, column);
}
}
bool
ovsdb_column_set_contains(const struct ovsdb_column_set *set,
unsigned int column_index)
{
size_t i;
for (i = 0; i < set->n_columns; i++) {
if (set->columns[i]->index == column_index) {
return true;
}
}
return false;
}
/* This comparison is sensitive to ordering of columns within a set, but that's
* good: the only existing caller wants to make sure that hash values are
* comparable, which is only true if column ordering is the same. */
bool
ovsdb_column_set_equals(const struct ovsdb_column_set *a,
const struct ovsdb_column_set *b)
{
size_t i;
if (a->n_columns != b->n_columns) {
return false;
}
for (i = 0; i < a->n_columns; i++) {
if (a->columns[i] != b->columns[i]) {
return false;
}
}
return true;
}

84
ovsdb/column.h Normal file
View File

@@ -0,0 +1,84 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_COLUMN_H
#define OVSDB_COLUMN_H 1
#include <stdbool.h>
#include "compiler.h"
#include "ovsdb-types.h"
struct ovsdb_table;
/* A column or a column schema (currently there is no distinction). */
struct ovsdb_column {
unsigned int index;
char *name;
char *comment;
bool mutable;
bool persistent;
struct ovsdb_type type;
};
/* A few columns appear in every table with standardized column indexes.
* These macros define those columns' indexes.
*
* Don't change these values, because ovsdb_query() depends on OVSDB_COL_UUID
* having value 0. */
enum {
OVSDB_COL_UUID = 0, /* UUID for the row. */
OVSDB_COL_VERSION = 1, /* Version number for the row. */
OVSDB_N_STD_COLUMNS
};
struct ovsdb_column *ovsdb_column_create(
const char *name, const char *comment, bool mutable, bool persistent,
const struct ovsdb_type *);
void ovsdb_column_destroy(struct ovsdb_column *);
struct ovsdb_error *ovsdb_column_from_json(const struct json *,
const char *name,
struct ovsdb_column **)
WARN_UNUSED_RESULT;
struct json *ovsdb_column_to_json(const struct ovsdb_column *);
/* An unordered set of distinct columns. */
struct ovsdb_column_set {
const struct ovsdb_column **columns;
size_t n_columns, allocated_columns;
};
#define OVSDB_COLUMN_SET_INITIALIZER { NULL, 0, 0 }
void ovsdb_column_set_init(struct ovsdb_column_set *);
void ovsdb_column_set_destroy(struct ovsdb_column_set *);
void ovsdb_column_set_clone(struct ovsdb_column_set *,
const struct ovsdb_column_set *);
struct ovsdb_error *ovsdb_column_set_from_json(const struct json *,
const struct ovsdb_table *,
struct ovsdb_column_set *);
void ovsdb_column_set_add(struct ovsdb_column_set *,
const struct ovsdb_column *);
void ovsdb_column_set_add_all(struct ovsdb_column_set *,
const struct ovsdb_table *);
bool ovsdb_column_set_contains(const struct ovsdb_column_set *,
unsigned int column_index);
bool ovsdb_column_set_equals(const struct ovsdb_column_set *,
const struct ovsdb_column_set *);
#endif /* column.h */

284
ovsdb/condition.c Normal file
View File

@@ -0,0 +1,284 @@
/* Copyright (c) 2009 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 "condition.h"
#include <limits.h>
#include "column.h"
#include "json.h"
#include "ovsdb-error.h"
#include "row.h"
#include "table.h"
struct ovsdb_error *
ovsdb_function_from_string(const char *name, enum ovsdb_function *function)
{
#define OVSDB_FUNCTION(ENUM, NAME) \
if (!strcmp(name, NAME)) { \
*function = ENUM; \
return NULL; \
}
OVSDB_FUNCTIONS;
#undef OVSDB_FUNCTION
return ovsdb_syntax_error(NULL, "unknown function",
"No function named %s.", name);
}
const char *
ovsdb_function_to_string(enum ovsdb_function function)
{
switch (function) {
#define OVSDB_FUNCTION(ENUM, NAME) case ENUM: return NAME;
OVSDB_FUNCTIONS;
#undef OVSDB_FUNCTION
}
return NULL;
}
static WARN_UNUSED_RESULT struct ovsdb_error *
ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
const struct json *json,
const struct ovsdb_symbol_table *symtab,
struct ovsdb_clause *clause)
{
const struct json_array *array;
struct ovsdb_error *error;
const char *function_name;
const char *column_name;
struct ovsdb_type type;
if (json->type != JSON_ARRAY
|| json->u.array.n != 3
|| json->u.array.elems[0]->type != JSON_STRING
|| json->u.array.elems[1]->type != JSON_STRING) {
return ovsdb_syntax_error(json, NULL, "Parse error in condition.");
}
array = json_array(json);
column_name = json_string(array->elems[0]);
clause->column = ovsdb_table_schema_get_column(ts, column_name);
if (!clause->column) {
return ovsdb_syntax_error(json, "unknown column",
"No column %s in table %s.",
column_name, ts->name);
}
type = clause->column->type;
function_name = json_string(array->elems[1]);
error = ovsdb_function_from_string(function_name, &clause->function);
if (error) {
return error;
}
/* Type-check and relax restrictions on 'type' if appropriate. */
switch (clause->function) {
case OVSDB_F_LT:
case OVSDB_F_LE:
case OVSDB_F_GT:
case OVSDB_F_GE:
/* XXX should we also allow these operators for types with n_min == 0,
* n_max == 1? (They would always be "false" if the value was
* missing.) */
if (!ovsdb_type_is_scalar(&type)
|| (type.key_type != OVSDB_TYPE_INTEGER
&& type.key_type != OVSDB_TYPE_REAL)) {
char *s = ovsdb_type_to_english(&type);
error = ovsdb_syntax_error(
json, NULL, "Type mismatch: \"%s\" operator may not be "
"applied to column %s of type %s.",
ovsdb_function_to_string(clause->function),
clause->column->name, s);
free(s);
return error;
}
break;
case OVSDB_F_EQ:
case OVSDB_F_NE:
break;
case OVSDB_F_EXCLUDES:
if (!ovsdb_type_is_scalar(&type)) {
type.n_min = 0;
type.n_max = UINT_MAX;
}
break;
case OVSDB_F_INCLUDES:
if (!ovsdb_type_is_scalar(&type)) {
type.n_min = 0;
}
break;
}
return ovsdb_datum_from_json(&clause->arg, &type, array->elems[2], symtab);
}
static void
ovsdb_clause_free(struct ovsdb_clause *clause)
{
ovsdb_datum_destroy(&clause->arg, &clause->column->type);
}
static int
compare_clauses_3way(const void *a_, const void *b_)
{
const struct ovsdb_clause *a = a_;
const struct ovsdb_clause *b = b_;
if (a->function != b->function) {
/* Bring functions to the front based on the fraction of table rows
* that they are (heuristically) expected to leave in the query
* results. Note that "enum ovsdb_function" is intentionally ordered
* to make this trivial. */
return a->function < b->function ? -1 : 1;
} else if (a->column->index != b->column->index) {
if (a->column->index < OVSDB_N_STD_COLUMNS
|| b->column->index < OVSDB_N_STD_COLUMNS) {
/* Bring the standard columns and in particular the UUID column
* (since OVSDB_COL_UUID has value 0) to the front. We have an
* index on the UUID column, so that makes our queries cheaper. */
return a->column->index < b->column->index ? -1 : 1;
} else {
/* Order clauses predictably to make testing easier. */
return strcmp(a->column->name, b->column->name);
}
} else {
return 0;
}
}
struct ovsdb_error *
ovsdb_condition_from_json(const struct ovsdb_table_schema *ts,
const struct json *json,
const struct ovsdb_symbol_table *symtab,
struct ovsdb_condition *cnd)
{
const struct json_array *array = json_array(json);
size_t i;
cnd->clauses = xmalloc(array->n * sizeof *cnd->clauses);
cnd->n_clauses = 0;
for (i = 0; i < array->n; i++) {
struct ovsdb_error *error;
error = ovsdb_clause_from_json(ts, array->elems[i], symtab,
&cnd->clauses[i]);
if (error) {
ovsdb_condition_destroy(cnd);
cnd->clauses = NULL;
cnd->n_clauses = 0;
return error;
}
cnd->n_clauses++;
}
/* A real database would have a query optimizer here. */
qsort(cnd->clauses, cnd->n_clauses, sizeof *cnd->clauses,
compare_clauses_3way);
return NULL;
}
static struct json *
ovsdb_clause_to_json(const struct ovsdb_clause *clause)
{
return json_array_create_3(
json_string_create(clause->column->name),
json_string_create(ovsdb_function_to_string(clause->function)),
ovsdb_datum_to_json(&clause->arg, &clause->column->type));
}
struct json *
ovsdb_condition_to_json(const struct ovsdb_condition *cnd)
{
struct json **clauses;
size_t i;
clauses = xmalloc(cnd->n_clauses * sizeof *clauses);
for (i = 0; i < cnd->n_clauses; i++) {
clauses[i] = ovsdb_clause_to_json(&cnd->clauses[i]);
}
return json_array_create(clauses, cnd->n_clauses);
}
bool
ovsdb_condition_evaluate(const struct ovsdb_row *row,
const struct ovsdb_condition *cnd)
{
size_t i;
for (i = 0; i < cnd->n_clauses; i++) {
const struct ovsdb_clause *c = &cnd->clauses[i];
const struct ovsdb_datum *field = &row->fields[c->column->index];
const struct ovsdb_datum *arg = &cnd->clauses[i].arg;
const struct ovsdb_type *type = &c->column->type;
if (ovsdb_type_is_scalar(type)) {
int cmp = ovsdb_atom_compare_3way(&field->keys[0], &arg->keys[0],
type->key_type);
switch (c->function) {
case OVSDB_F_LT:
return cmp < 0;
case OVSDB_F_LE:
return cmp <= 0;
case OVSDB_F_EQ:
case OVSDB_F_INCLUDES:
return cmp == 0;
case OVSDB_F_NE:
case OVSDB_F_EXCLUDES:
return cmp != 0;
case OVSDB_F_GE:
return cmp >= 0;
case OVSDB_F_GT:
return cmp > 0;
}
} else {
switch (c->function) {
case OVSDB_F_EQ:
return ovsdb_datum_equals(field, arg, type);
case OVSDB_F_NE:
return !ovsdb_datum_equals(field, arg, type);
case OVSDB_F_INCLUDES:
return ovsdb_datum_includes_all(arg, field, type);
case OVSDB_F_EXCLUDES:
return ovsdb_datum_excludes_all(arg, field, type);
case OVSDB_F_LT:
case OVSDB_F_LE:
case OVSDB_F_GE:
case OVSDB_F_GT:
NOT_REACHED();
}
}
NOT_REACHED();
}
return true;
}
void
ovsdb_condition_destroy(struct ovsdb_condition *cnd)
{
size_t i;
for (i = 0; i < cnd->n_clauses; i++) {
ovsdb_clause_free(&cnd->clauses[i]);
}
free(cnd->clauses);
}

72
ovsdb/condition.h Normal file
View File

@@ -0,0 +1,72 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_CONDITION_H
#define OVSDB_CONDITION_H 1
#include <stddef.h>
#include "compiler.h"
#include "ovsdb-data.h"
struct json;
struct ovsdb_table_schema;
struct ovsdb_row;
/* These list is ordered in ascending order of the fraction of tables row that
* they are (heuristically) expected to leave in query results. */
#define OVSDB_FUNCTIONS \
OVSDB_FUNCTION(OVSDB_F_EQ, "==") \
OVSDB_FUNCTION(OVSDB_F_INCLUDES, "includes") \
OVSDB_FUNCTION(OVSDB_F_LE, "<=") \
OVSDB_FUNCTION(OVSDB_F_LT, "<") \
OVSDB_FUNCTION(OVSDB_F_GE, ">=") \
OVSDB_FUNCTION(OVSDB_F_GT, ">") \
OVSDB_FUNCTION(OVSDB_F_EXCLUDES, "excludes") \
OVSDB_FUNCTION(OVSDB_F_NE, "!=")
enum ovsdb_function {
#define OVSDB_FUNCTION(ENUM, NAME) ENUM,
OVSDB_FUNCTIONS
#undef OVSDB_FUNCTION
};
struct ovsdb_error *ovsdb_function_from_string(const char *,
enum ovsdb_function *)
WARN_UNUSED_RESULT;
const char *ovsdb_function_to_string(enum ovsdb_function);
struct ovsdb_clause {
enum ovsdb_function function;
const struct ovsdb_column *column;
struct ovsdb_datum arg;
};
struct ovsdb_condition {
struct ovsdb_clause *clauses;
size_t n_clauses;
};
#define OVSDB_CONDITION_INITIALIZER { NULL, 0 }
struct ovsdb_error *ovsdb_condition_from_json(
const struct ovsdb_table_schema *,
const struct json *, const struct ovsdb_symbol_table *,
struct ovsdb_condition *) WARN_UNUSED_RESULT;
struct json *ovsdb_condition_to_json(const struct ovsdb_condition *);
void ovsdb_condition_destroy(struct ovsdb_condition *);
bool ovsdb_condition_evaluate(const struct ovsdb_row *,
const struct ovsdb_condition *);
#endif /* ovsdb/condition.h */

613
ovsdb/execution.c Normal file
View File

@@ -0,0 +1,613 @@
/* Copyright (c) 2009 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 <assert.h>
#include <limits.h>
#include "column.h"
#include "condition.h"
#include "file.h"
#include "json.h"
#include "ovsdb-data.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "ovsdb.h"
#include "query.h"
#include "row.h"
#include "table.h"
#include "timeval.h"
#include "transaction.h"
struct ovsdb_execution {
struct ovsdb *db;
struct ovsdb_txn *txn;
struct ovsdb_symbol_table *symtab;
bool durable;
/* Triggers. */
long long int elapsed_msec;
long long int timeout_msec;
};
typedef struct ovsdb_error *ovsdb_operation_executor(struct ovsdb_execution *,
struct ovsdb_parser *,
struct json *result);
static struct ovsdb_error *do_commit(struct ovsdb_execution *);
static ovsdb_operation_executor ovsdb_execute_insert;
static ovsdb_operation_executor ovsdb_execute_select;
static ovsdb_operation_executor ovsdb_execute_update;
static ovsdb_operation_executor ovsdb_execute_delete;
static ovsdb_operation_executor ovsdb_execute_wait;
static ovsdb_operation_executor ovsdb_execute_commit;
static ovsdb_operation_executor ovsdb_execute_abort;
static ovsdb_operation_executor *
lookup_executor(const char *name)
{
struct ovsdb_operation {
const char *name;
ovsdb_operation_executor *executor;
};
static const struct ovsdb_operation operations[] = {
{ "insert", ovsdb_execute_insert },
{ "select", ovsdb_execute_select },
{ "update", ovsdb_execute_update },
{ "delete", ovsdb_execute_delete },
{ "wait", ovsdb_execute_wait },
{ "commit", ovsdb_execute_commit },
{ "abort", ovsdb_execute_abort },
};
size_t i;
for (i = 0; i < ARRAY_SIZE(operations); i++) {
const struct ovsdb_operation *c = &operations[i];
if (!strcmp(c->name, name)) {
return c->executor;
}
}
return NULL;
}
struct json *
ovsdb_execute(struct ovsdb *db, const struct json *params,
long long int elapsed_msec, long long int *timeout_msec)
{
struct ovsdb_execution x;
struct ovsdb_error *error;
struct json *results;
size_t n_operations;
size_t i;
if (params->type != JSON_ARRAY) {
struct ovsdb_error *error;
error = ovsdb_syntax_error(params, NULL, "array expected");
results = ovsdb_error_to_json(error);
ovsdb_error_destroy(error);
return results;
}
x.db = db;
x.txn = ovsdb_txn_create(db);
x.symtab = ovsdb_symbol_table_create();
x.durable = false;
x.elapsed_msec = elapsed_msec;
x.timeout_msec = LLONG_MAX;
results = NULL;
results = json_array_create_empty();
n_operations = params->u.array.n;
error = NULL;
for (i = 0; i < n_operations; i++) {
struct json *operation = params->u.array.elems[i];
struct ovsdb_error *parse_error;
struct ovsdb_parser parser;
struct json *result;
const struct json *op;
/* Parse and execute operation. */
ovsdb_parser_init(&parser, operation,
"ovsdb operation %zu of %zu", i + 1, n_operations);
op = ovsdb_parser_member(&parser, "op", OP_ID);
result = json_object_create();
if (op) {
const char *op_name = json_string(op);
ovsdb_operation_executor *executor = lookup_executor(op_name);
if (executor) {
error = executor(&x, &parser, result);
} else {
error = ovsdb_syntax_error(operation, "unknown operation",
"No operation \"%s\"", op_name);
}
} else {
assert(ovsdb_parser_has_error(&parser));
}
/* A parse error overrides any other error.
* An error overrides any other result. */
parse_error = ovsdb_parser_finish(&parser);
if (parse_error) {
ovsdb_error_destroy(error);
error = parse_error;
}
if (error) {
json_destroy(result);
result = ovsdb_error_to_json(error);
}
if (error && !strcmp(ovsdb_error_get_tag(error), "not supported")
&& timeout_msec) {
ovsdb_txn_abort(x.txn);
*timeout_msec = x.timeout_msec;
ovsdb_error_destroy(error);
json_destroy(results);
return NULL;
}
/* Add result to array. */
json_array_add(results, result);
if (error) {
break;
}
}
if (!error) {
/* Commit transaction. Bail if commit encounters error. */
error = do_commit(&x);
if (error) {
json_array_add(results, ovsdb_error_to_json(error));
}
} else {
ovsdb_txn_abort(x.txn);
}
while (json_array(results)->n < n_operations) {
json_array_add(results, json_null_create());
}
ovsdb_error_destroy(error);
ovsdb_symbol_table_destroy(x.symtab);
return results;
}
struct ovsdb_error *
ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result UNUSED)
{
const struct json *durable;
durable = ovsdb_parser_member(parser, "durable", OP_BOOLEAN);
if (durable && json_boolean(durable)) {
x->durable = true;
}
return NULL;
}
static struct ovsdb_error *
ovsdb_execute_abort(struct ovsdb_execution *x UNUSED,
struct ovsdb_parser *parser UNUSED,
struct json *result UNUSED)
{
return ovsdb_error("aborted", "aborted by request");
}
static struct ovsdb_error *
do_commit(struct ovsdb_execution *x)
{
if (x->db->file) {
struct ovsdb_error *error;
struct json *json;
json = ovsdb_txn_to_json(x->txn);
if (!json) {
/* Nothing to commit. */
return NULL;
}
error = ovsdb_file_write(x->db->file, json);
json_destroy(json);
if (error) {
return ovsdb_wrap_error(error, "writing transaction failed");
}
if (x->durable) {
error = ovsdb_file_commit(x->db->file);
if (error) {
return ovsdb_wrap_error(error,
"committing transaction failed");
}
}
}
ovsdb_txn_commit(x->txn);
return NULL;
}
static struct ovsdb_table *
parse_table(struct ovsdb_execution *x,
struct ovsdb_parser *parser, const char *member)
{
struct ovsdb_table *table;
const char *table_name;
const struct json *json;
json = ovsdb_parser_member(parser, member, OP_ID);
if (!json) {
return NULL;
}
table_name = json_string(json);
table = shash_find_data(&x->db->tables, table_name);
if (!table) {
ovsdb_parser_raise_error(parser, "No table named %s.", table_name);
}
return table;
}
static WARN_UNUSED_RESULT struct ovsdb_error *
parse_row(struct ovsdb_parser *parser, const char *member,
const struct ovsdb_table *table,
const struct ovsdb_symbol_table *symtab,
struct ovsdb_row **rowp, struct ovsdb_column_set *columns)
{
struct ovsdb_error *error;
const struct json *json;
struct ovsdb_row *row;
*rowp = NULL;
if (!table) {
return OVSDB_BUG("null table");
}
json = ovsdb_parser_member(parser, member, OP_OBJECT);
if (!json) {
return OVSDB_BUG("null row member");
}
row = ovsdb_row_create(table);
error = ovsdb_row_from_json(row, json, symtab, columns);
if (error) {
ovsdb_row_destroy(row);
return error;
} else {
*rowp = row;
return NULL;
}
}
struct ovsdb_error *
ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result)
{
struct ovsdb_table *table;
struct ovsdb_row *row = NULL;
const struct json *uuid_name;
struct ovsdb_error *error;
table = parse_table(x, parser, "table");
uuid_name = ovsdb_parser_member(parser, "uuid-name", OP_ID | OP_OPTIONAL);
error = ovsdb_parser_get_error(parser);
if (!error) {
error = parse_row(parser, "row", table, x->symtab, &row, NULL);
}
if (!error) {
uuid_generate(ovsdb_row_get_uuid_rw(row));
if (uuid_name) {
ovsdb_symbol_table_put(x->symtab, json_string(uuid_name),
ovsdb_row_get_uuid(row));
}
ovsdb_txn_row_insert(x->txn, row);
json_object_put(result, "uuid",
ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID],
&ovsdb_type_uuid));
row = NULL;
}
return error;
}
struct ovsdb_error *
ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result)
{
struct ovsdb_table *table;
const struct json *where, *columns_json, *sort_json;
struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
struct ovsdb_column_set sort = OVSDB_COLUMN_SET_INITIALIZER;
struct ovsdb_error *error;
table = parse_table(x, parser, "table");
where = ovsdb_parser_member(parser, "where", OP_ARRAY);
columns_json = ovsdb_parser_member(parser, "columns",
OP_ARRAY | OP_OPTIONAL);
sort_json = ovsdb_parser_member(parser, "sort", OP_ARRAY | OP_OPTIONAL);
error = ovsdb_parser_get_error(parser);
if (!error) {
error = ovsdb_condition_from_json(table->schema, where, x->symtab,
&condition);
}
if (!error) {
error = ovsdb_column_set_from_json(columns_json, table, &columns);
}
if (!error) {
error = ovsdb_column_set_from_json(sort_json, table, &sort);
}
if (!error) {
struct ovsdb_row_set rows = OVSDB_ROW_SET_INITIALIZER;
ovsdb_query_distinct(table, &condition, &columns, &rows);
ovsdb_row_set_sort(&rows, &sort);
json_object_put(result, "rows",
ovsdb_row_set_to_json(&rows, &columns));
ovsdb_row_set_destroy(&rows);
}
ovsdb_column_set_destroy(&columns);
ovsdb_column_set_destroy(&sort);
ovsdb_condition_destroy(&condition);
return error;
}
struct update_row_cbdata {
size_t n_matches;
struct ovsdb_txn *txn;
const struct ovsdb_row *row;
const struct ovsdb_column_set *columns;
};
static bool
update_row_cb(const struct ovsdb_row *row, void *ur_)
{
struct update_row_cbdata *ur = ur_;
ur->n_matches++;
if (!ovsdb_row_equal_columns(row, ur->row, ur->columns)) {
ovsdb_row_update_columns(ovsdb_txn_row_modify(ur->txn, row),
ur->row, ur->columns);
}
return true;
}
struct ovsdb_error *
ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result)
{
struct ovsdb_table *table;
const struct json *where;
struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
struct ovsdb_row *row = NULL;
struct update_row_cbdata ur;
struct ovsdb_error *error;
table = parse_table(x, parser, "table");
where = ovsdb_parser_member(parser, "where", OP_ARRAY);
error = ovsdb_parser_get_error(parser);
if (!error) {
error = parse_row(parser, "row", table, x->symtab, &row, &columns);
}
if (!error) {
error = ovsdb_condition_from_json(table->schema, where, x->symtab,
&condition);
}
if (!error) {
ur.n_matches = 0;
ur.txn = x->txn;
ur.row = row;
ur.columns = &columns;
ovsdb_query(table, &condition, update_row_cb, &ur);
json_object_put(result, "count", json_integer_create(ur.n_matches));
}
ovsdb_row_destroy(row);
ovsdb_column_set_destroy(&columns);
ovsdb_condition_destroy(&condition);
return error;
}
struct delete_row_cbdata {
size_t n_matches;
const struct ovsdb_table *table;
struct ovsdb_txn *txn;
};
static bool
delete_row_cb(const struct ovsdb_row *row, void *dr_)
{
struct delete_row_cbdata *dr = dr_;
dr->n_matches++;
ovsdb_txn_row_delete(dr->txn, row);
return true;
}
struct ovsdb_error *
ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result)
{
struct ovsdb_table *table;
const struct json *where;
struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
struct ovsdb_error *error;
where = ovsdb_parser_member(parser, "where", OP_ARRAY);
table = parse_table(x, parser, "table");
error = ovsdb_parser_get_error(parser);
if (!error) {
error = ovsdb_condition_from_json(table->schema, where, x->symtab,
&condition);
}
if (!error) {
struct delete_row_cbdata dr;
dr.n_matches = 0;
dr.table = table;
dr.txn = x->txn;
ovsdb_query(table, &condition, delete_row_cb, &dr);
json_object_put(result, "count", json_integer_create(dr.n_matches));
}
ovsdb_condition_destroy(&condition);
return error;
}
struct wait_auxdata {
struct ovsdb_row_hash *actual;
struct ovsdb_row_hash *expected;
bool *equal;
};
static bool
ovsdb_execute_wait_query_cb(const struct ovsdb_row *row, void *aux_)
{
struct wait_auxdata *aux = aux_;
if (ovsdb_row_hash_contains(aux->expected, row)) {
ovsdb_row_hash_insert(aux->actual, row);
return true;
} else {
/* The query row isn't in the expected result set, so the actual and
* expected results sets definitely differ and we can short-circuit the
* rest of the query. */
*aux->equal = false;
return false;
}
}
static struct ovsdb_error *
ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result UNUSED)
{
struct ovsdb_table *table;
const struct json *timeout, *where, *columns_json, *until, *rows;
struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
struct ovsdb_row_hash expected = OVSDB_ROW_HASH_INITIALIZER(expected);
struct ovsdb_row_hash actual = OVSDB_ROW_HASH_INITIALIZER(actual);
struct ovsdb_error *error;
struct wait_auxdata aux;
long long int timeout_msec = 0;
size_t i;
timeout = ovsdb_parser_member(parser, "timeout", OP_NUMBER | OP_OPTIONAL);
where = ovsdb_parser_member(parser, "where", OP_ARRAY);
columns_json = ovsdb_parser_member(parser, "columns",
OP_ARRAY | OP_OPTIONAL);
until = ovsdb_parser_member(parser, "until", OP_STRING);
rows = ovsdb_parser_member(parser, "rows", OP_ARRAY);
table = parse_table(x, parser, "table");
error = ovsdb_parser_get_error(parser);
if (!error) {
error = ovsdb_condition_from_json(table->schema, where, x->symtab,
&condition);
}
if (!error) {
error = ovsdb_column_set_from_json(columns_json, table, &columns);
}
if (!error) {
if (timeout) {
timeout_msec = MIN(LLONG_MAX, json_real(timeout));
if (timeout_msec < 0) {
error = ovsdb_syntax_error(timeout, NULL,
"timeout must be nonnegative");
} else if (timeout_msec < x->timeout_msec) {
x->timeout_msec = timeout_msec;
}
} else {
timeout_msec = LLONG_MAX;
}
if (strcmp(json_string(until), "==")
&& strcmp(json_string(until), "!=")) {
error = ovsdb_syntax_error(until, NULL,
"\"until\" must be \"==\" or \"!=\"");
}
}
if (!error) {
/* Parse "rows" into 'expected'. */
ovsdb_row_hash_init(&expected, &columns);
for (i = 0; i < rows->u.array.n; i++) {
struct ovsdb_error *error;
struct ovsdb_row *row;
row = ovsdb_row_create(table);
error = ovsdb_row_from_json(row, rows->u.array.elems[i], x->symtab,
NULL);
if (error) {
break;
}
if (!ovsdb_row_hash_insert(&expected, row)) {
/* XXX Perhaps we should abort with an error or log a
* warning. */
ovsdb_row_destroy(row);
}
}
}
if (!error) {
/* Execute query. */
bool equal = true;
ovsdb_row_hash_init(&actual, &columns);
aux.actual = &actual;
aux.expected = &expected;
aux.equal = &equal;
ovsdb_query(table, &condition, ovsdb_execute_wait_query_cb, &aux);
if (equal) {
/* We know that every row in 'actual' is also in 'expected'. We
* also know that all of the rows in 'actual' are distinct and that
* all of the rows in 'expected' are distinct. Therefore, if
* 'actual' and 'expected' have the same number of rows, then they
* have the same content. */
size_t n_actual = ovsdb_row_hash_count(&actual);
size_t n_expected = ovsdb_row_hash_count(&expected);
equal = n_actual == n_expected;
}
if (!strcmp(json_string(until), "==") != equal) {
if (timeout && x->elapsed_msec >= timeout_msec) {
if (x->elapsed_msec) {
error = ovsdb_error("timed out",
"\"wait\" timed out after %lld ms",
x->elapsed_msec);
} else {
error = ovsdb_error("timed out", "\"wait\" timed out");
}
} else {
/* ovsdb_execute() will change this, if triggers really are
* supported. */
error = ovsdb_error("not supported", "triggers not supported");
}
}
}
ovsdb_row_hash_destroy(&expected, true);
ovsdb_row_hash_destroy(&actual, false);
ovsdb_column_set_destroy(&columns);
ovsdb_condition_destroy(&condition);
return error;
}

360
ovsdb/file.c Normal file
View File

@@ -0,0 +1,360 @@
/* Copyright (c) 2009 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 "file.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "json.h"
#include "lockfile.h"
#include "ovsdb-error.h"
#include "sha1.h"
#include "util.h"
#define THIS_MODULE VLM_ovsdb_file
#include "vlog.h"
enum ovsdb_file_mode {
OVSDB_FILE_READ,
OVSDB_FILE_WRITE
};
struct ovsdb_file {
off_t offset;
char *name;
struct lockfile *lockfile;
FILE *stream;
struct ovsdb_error *read_error;
struct ovsdb_error *write_error;
enum ovsdb_file_mode mode;
};
struct ovsdb_error *
ovsdb_file_open(const char *name, int flags, struct ovsdb_file **filep)
{
struct lockfile *lockfile;
struct ovsdb_error *error;
struct ovsdb_file *file;
struct stat s;
FILE *stream;
int accmode;
int fd;
*filep = NULL;
accmode = flags & O_ACCMODE;
if (accmode == O_RDWR || accmode == O_WRONLY) {
int retval = lockfile_lock(name, 0, &lockfile);
if (retval) {
error = ovsdb_io_error(retval, "%s: failed to lock lockfile",
name);
goto error;
}
} else {
lockfile = NULL;
}
fd = open(name, flags, 0666);
if (fd < 0) {
const char *op = flags & O_CREAT && flags & O_EXCL ? "create" : "open";
error = ovsdb_io_error(errno, "%s: %s failed", op, name);
goto error_unlock;
}
if (!fstat(fd, &s) && s.st_size == 0) {
/* It's (probably) a new file so fsync() its parent directory to ensure
* that its directory entry is committed to disk. */
char *dir = dir_name(name);
int dirfd = open(dir, O_RDONLY);
if (dirfd >= 0) {
if (fsync(dirfd) && errno != EINVAL) {
VLOG_ERR("%s: fsync failed (%s)", dir, strerror(errno));
}
close(dirfd);
} else {
VLOG_ERR("%s: open failed (%s)", dir, strerror(errno));
}
free(dir);
}
stream = fdopen(fd, (accmode == O_RDONLY ? "rb"
: accmode == O_WRONLY ? "wb"
: "w+b"));
if (!stream) {
error = ovsdb_io_error(errno, "%s: fdopen failed", name);
goto error_close;
}
file = xmalloc(sizeof *file);
file->name = xstrdup(name);
file->lockfile = lockfile;
file->stream = stream;
file->offset = 0;
file->read_error = NULL;
file->write_error = NULL;
file->mode = OVSDB_FILE_READ;
*filep = file;
return NULL;
error_close:
close(fd);
error_unlock:
lockfile_unlock(lockfile);
error:
return error;
}
void
ovsdb_file_close(struct ovsdb_file *file)
{
if (file) {
free(file->name);
fclose(file->stream);
lockfile_unlock(file->lockfile);
ovsdb_error_destroy(file->read_error);
ovsdb_error_destroy(file->write_error);
free(file);
}
}
static const char magic[] = "OVSDB JSON ";
static bool
parse_header(char *header, unsigned long int *length,
uint8_t sha1[SHA1_DIGEST_SIZE])
{
char *p;
/* 'header' must consist of a magic string... */
if (strncmp(header, magic, strlen(magic))) {
return false;
}
/* ...followed by a length in bytes... */
*length = strtoul(header + strlen(magic), &p, 10);
if (!*length || *length == ULONG_MAX || *p != ' ') {
return false;
}
p++;
/* ...followed by a SHA-1 hash... */
if (!sha1_from_hex(sha1, p)) {
return false;
}
p += SHA1_HEX_DIGEST_LEN;
/* ...and ended by a new-line. */
if (*p != '\n') {
return false;
}
return true;
}
struct ovsdb_file_read_cbdata {
char input[4096];
struct ovsdb_file *file;
int error;
unsigned long length;
};
static struct ovsdb_error *
parse_body(struct ovsdb_file *file, off_t offset, unsigned long int length,
uint8_t sha1[SHA1_DIGEST_SIZE], struct json **jsonp)
{
unsigned long int bytes_left;
struct json_parser *parser;
struct sha1_ctx ctx;
sha1_init(&ctx);
parser = json_parser_create(JSPF_TRAILER);
bytes_left = length;
while (length > 0) {
char input[BUFSIZ];
int chunk;
chunk = MIN(length, sizeof input);
if (fread(input, 1, chunk, file->stream) != chunk) {
json_parser_abort(parser);
return ovsdb_io_error(ferror(file->stream) ? errno : EOF,
"%s: error reading %lu bytes "
"starting at offset %lld", file->name,
length, (long long int) offset);
}
sha1_update(&ctx, input, chunk);
json_parser_feed(parser, input, chunk);
length -= chunk;
}
sha1_final(&ctx, sha1);
*jsonp = json_parser_finish(parser);
return NULL;
}
struct ovsdb_error *
ovsdb_file_read(struct ovsdb_file *file, struct json **jsonp)
{
uint8_t expected_sha1[SHA1_DIGEST_SIZE];
uint8_t actual_sha1[SHA1_DIGEST_SIZE];
struct ovsdb_error *error;
off_t data_offset;
unsigned long data_length;
struct json *json;
char header[128];
*jsonp = json = NULL;
if (file->read_error) {
return ovsdb_error_clone(file->read_error);
} else if (file->mode == OVSDB_FILE_WRITE) {
return OVSDB_BUG("reading file in write mode");
}
if (!fgets(header, sizeof header, file->stream)) {
if (feof(file->stream)) {
error = NULL;
} else {
error = ovsdb_io_error(errno, "%s: read failed", file->name);
}
goto error;
}
if (!parse_header(header, &data_length, expected_sha1)) {
error = ovsdb_syntax_error(NULL, NULL, "%s: parse error at offset "
"%lld in header line \"%.*s\"",
file->name, (long long int) file->offset,
(int) strcspn(header, "\n"), header);
goto error;
}
data_offset = file->offset + strlen(header);
error = parse_body(file, data_offset, data_length, actual_sha1, &json);
if (error) {
goto error;
}
if (memcmp(expected_sha1, actual_sha1, SHA1_DIGEST_SIZE)) {
error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at "
"offset %lld have SHA-1 hash "SHA1_FMT" "
"but should have hash "SHA1_FMT,
file->name, data_length,
(long long int) data_offset,
SHA1_ARGS(actual_sha1),
SHA1_ARGS(expected_sha1));
goto error;
}
if (json->type == JSON_STRING) {
error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at "
"offset %lld are not valid JSON (%s)",
file->name, data_length,
(long long int) data_offset,
json->u.string);
goto error;
}
file->offset = data_offset + data_length;
*jsonp = json;
return 0;
error:
file->read_error = ovsdb_error_clone(error);
json_destroy(json);
return error;
}
struct ovsdb_error *
ovsdb_file_write(struct ovsdb_file *file, struct json *json)
{
uint8_t sha1[SHA1_DIGEST_SIZE];
struct ovsdb_error *error;
char *json_string;
char header[128];
size_t length;
json_string = NULL;
if (file->write_error) {
return ovsdb_error_clone(file->write_error);
} else if (file->mode == OVSDB_FILE_READ) {
file->mode = OVSDB_FILE_WRITE;
if (fseeko(file->stream, file->offset, SEEK_SET)) {
error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld",
file->name, (long long int) file->offset);
goto error;
}
if (ftruncate(fileno(file->stream), file->offset)) {
error = ovsdb_io_error(errno, "%s: cannot truncate to length %lld",
file->name, (long long int) file->offset);
goto error;
}
}
if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) {
error = OVSDB_BUG("bad JSON type");
goto error;
}
/* Compose content. Add a new-line (replacing the null terminator) to make
* the file easier to read, even though it has no semantic value. */
json_string = json_to_string(json, 0);
length = strlen(json_string) + 1;
json_string[length - 1] = '\n';
/* Compose header. */
sha1_bytes(json_string, length, sha1);
snprintf(header, sizeof header, "%s%zu "SHA1_FMT"\n",
magic, length, SHA1_ARGS(sha1));
/* Write. */
if (fwrite(header, strlen(header), 1, file->stream) != 1
|| fwrite(json_string, length, 1, file->stream) != 1
|| fflush(file->stream))
{
error = ovsdb_io_error(errno, "%s: write failed", file->name);
/* Remove any partially written data, ignoring errors since there is
* nothing further we can do. */
ftruncate(fileno(file->stream), file->offset);
goto error;
}
file->offset += strlen(header) + length;
free(json_string);
return 0;
error:
file->write_error = ovsdb_error_clone(error);
free(json_string);
return error;
}
struct ovsdb_error *
ovsdb_file_commit(struct ovsdb_file *file)
{
if (fsync(fileno(file->stream))) {
return ovsdb_io_error(errno, "%s: fsync failed", file->name);
}
return 0;
}

36
ovsdb/file.h Normal file
View File

@@ -0,0 +1,36 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_FILE_H
#define OVSDB_FILE_H 1
#include <sys/types.h>
#include "compiler.h"
struct json;
struct ovsdb_file;
struct ovsdb_error *ovsdb_file_open(const char *name, int flags,
struct ovsdb_file **) WARN_UNUSED_RESULT;
void ovsdb_file_close(struct ovsdb_file *);
struct ovsdb_error *ovsdb_file_read(struct ovsdb_file *, struct json **)
WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_file_write(struct ovsdb_file *, struct json *)
WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_file_commit(struct ovsdb_file *)
WARN_UNUSED_RESULT;
#endif /* ovsdb/file.h */

362
ovsdb/jsonrpc-server.c Normal file
View File

@@ -0,0 +1,362 @@
/* Copyright (c) 2009 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 "jsonrpc-server.h"
#include <errno.h>
#include "json.h"
#include "jsonrpc.h"
#include "ovsdb.h"
#include "stream.h"
#include "svec.h"
#include "timeval.h"
#include "trigger.h"
#define THIS_MODULE VLM_ovsdb_jsonrpc_server
#include "vlog.h"
struct ovsdb_jsonrpc_trigger {
struct ovsdb_trigger trigger;
struct ovsdb_jsonrpc_session *session;
struct hmap_node hmap_node; /* Element in session's trigger table. */
struct json *id;
};
static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find(
struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash);
static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *);
struct ovsdb_jsonrpc_session {
struct ovsdb_jsonrpc_server *server;
struct list node; /* Element in server's sessions list. */
struct jsonrpc *rpc;
struct hmap triggers;
struct list completions; /* Completed triggers. */
};
static void ovsdb_jsonrpc_session_open(struct ovsdb_jsonrpc_server *,
struct stream *);
static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *);
static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *,
struct jsonrpc_msg *);
static void ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *,
struct jsonrpc_msg *);
struct ovsdb_jsonrpc_server {
struct ovsdb *db;
struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */
unsigned int n_sessions, max_sessions;
unsigned int max_triggers;
struct pstream **listeners;
size_t n_listeners, allocated_listeners;
};
static void ovsdb_jsonrpc_server_listen(struct ovsdb_jsonrpc_server *,
struct pstream *);
int
ovsdb_jsonrpc_server_create(struct ovsdb *db, const struct svec *active,
const struct svec *passive,
struct ovsdb_jsonrpc_server **serverp)
{
struct ovsdb_jsonrpc_server *server;
const char *name;
int retval = 0;
size_t i;
server = xzalloc(sizeof *server);
server->db = db;
server->max_sessions = 64;
server->max_triggers = 64;
list_init(&server->sessions);
SVEC_FOR_EACH (i, name, active) {
struct stream *stream;
int error;
error = stream_open(name, &stream);
if (!error) {
ovsdb_jsonrpc_session_open(server, stream);
} else {
ovs_error(error, "%s: connection failed", name);
retval = error;
}
}
SVEC_FOR_EACH (i, name, passive) {
struct pstream *pstream;
int error;
error = pstream_open(name, &pstream);
if (!error) {
ovsdb_jsonrpc_server_listen(server, pstream);
} else {
ovs_error(error, "failed to listen on %s", name);
retval = error;
}
}
*serverp = server;
return retval;
}
void
ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
{
struct ovsdb_jsonrpc_session *s, *next;
size_t i;
/* Accept new connections. */
for (i = 0; i < svr->n_listeners && svr->n_sessions < svr->max_sessions;) {
struct pstream *listener = svr->listeners[i];
struct stream *stream;
int error;
error = pstream_accept(listener, &stream);
if (!error) {
ovsdb_jsonrpc_session_open(svr, stream);
} else if (error == EAGAIN) {
i++;
} else if (error) {
VLOG_WARN("%s: accept failed: %s",
pstream_get_name(listener), strerror(error));
pstream_close(listener);
svr->listeners[i] = svr->listeners[--svr->n_listeners];
}
}
/* Handle each session. */
LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node,
&svr->sessions) {
struct jsonrpc_msg *msg;
int error;
jsonrpc_run(s->rpc);
while (!list_is_empty(&s->completions)) {
struct ovsdb_jsonrpc_trigger *t
= CONTAINER_OF(s->completions.next,
struct ovsdb_jsonrpc_trigger, trigger.node);
ovsdb_jsonrpc_trigger_complete(t);
}
if (!jsonrpc_get_backlog(s->rpc) && !jsonrpc_recv(s->rpc, &msg)) {
if (msg->type == JSONRPC_REQUEST) {
ovsdb_jsonrpc_session_got_request(s, msg);
} else if (msg->type == JSONRPC_NOTIFY) {
ovsdb_jsonrpc_session_got_notify(s, msg);
} else {
VLOG_WARN("%s: received unexpected %s message",
jsonrpc_get_name(s->rpc),
jsonrpc_msg_type_to_string(msg->type));
jsonrpc_error(s->rpc, EPROTO);
jsonrpc_msg_destroy(msg);
}
}
error = jsonrpc_get_status(s->rpc);
if (error) {
ovsdb_jsonrpc_session_close(s);
}
}
}
void
ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
{
struct ovsdb_jsonrpc_session *s;
if (svr->n_sessions < svr->max_sessions) {
size_t i;
for (i = 0; i < svr->n_sessions; i++) {
pstream_wait(svr->listeners[i]);
}
}
LIST_FOR_EACH (s, struct ovsdb_jsonrpc_session, node, &svr->sessions) {
jsonrpc_wait(s->rpc);
if (!jsonrpc_get_backlog(s->rpc)) {
jsonrpc_recv_wait(s->rpc);
}
}
}
static void
ovsdb_jsonrpc_server_listen(struct ovsdb_jsonrpc_server *svr,
struct pstream *pstream)
{
if (svr->n_listeners >= svr->allocated_listeners) {
svr->listeners = x2nrealloc(svr->listeners, &svr->allocated_listeners,
sizeof *svr->listeners);
}
svr->listeners[svr->n_listeners++] = pstream;
}
static struct ovsdb_jsonrpc_trigger *
ovsdb_jsonrpc_trigger_find(struct ovsdb_jsonrpc_session *s,
const struct json *id, size_t hash)
{
struct ovsdb_jsonrpc_trigger *t;
HMAP_FOR_EACH_WITH_HASH (t, struct ovsdb_jsonrpc_trigger, hmap_node, hash,
&s->triggers) {
if (json_equal(t->id, id)) {
return t;
}
}
return NULL;
}
static void
ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t)
{
struct ovsdb_jsonrpc_session *s = t->session;
if (!jsonrpc_get_status(s->rpc)) {
struct jsonrpc_msg *reply;
struct json *result;
result = ovsdb_trigger_steal_result(&t->trigger);
if (result) {
reply = jsonrpc_create_reply(result, t->id);
} else {
reply = jsonrpc_create_error(json_string_create("canceled"),
t->id);
}
jsonrpc_send(s->rpc, reply);
}
json_destroy(t->id);
ovsdb_trigger_destroy(&t->trigger);
hmap_remove(&s->triggers, &t->hmap_node);
free(t);
}
static void
ovsdb_jsonrpc_session_open(struct ovsdb_jsonrpc_server *svr,
struct stream *stream)
{
struct ovsdb_jsonrpc_session *s;
s = xzalloc(sizeof *s);
s->server = svr;
list_push_back(&svr->sessions, &s->node);
s->rpc = jsonrpc_open(stream);
hmap_init(&s->triggers);
list_init(&s->completions);
}
static void
ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s)
{
struct ovsdb_jsonrpc_trigger *t, *next;
jsonrpc_error(s->rpc, EOF);
HMAP_FOR_EACH_SAFE (t, next, struct ovsdb_jsonrpc_trigger, hmap_node,
&s->triggers) {
ovsdb_jsonrpc_trigger_complete(t);
}
jsonrpc_close(s->rpc);
list_remove(&s->node);
s->server->n_sessions--;
}
static struct jsonrpc_msg *
execute_transaction(struct ovsdb_jsonrpc_session *s,
struct jsonrpc_msg *request)
{
struct ovsdb_jsonrpc_trigger *t;
size_t hash;
/* Check for duplicate ID. */
hash = json_hash(request->id, 0);
t = ovsdb_jsonrpc_trigger_find(s, request->id, hash);
if (t) {
return jsonrpc_create_error(
json_string_create("duplicate request ID"), request->id);
}
/* Insert into trigger table. */
t = xmalloc(sizeof *t);
ovsdb_trigger_init(s->server->db,
&t->trigger, request->params, &s->completions,
time_msec());
t->session = s;
t->id = request->id;
hmap_insert(&s->triggers, &t->hmap_node, hash);
request->id = NULL;
request->params = NULL;
/* Complete early if possible. */
if (ovsdb_trigger_is_complete(&t->trigger)) {
ovsdb_jsonrpc_trigger_complete(t);
}
return NULL;
}
static void
ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
struct jsonrpc_msg *request)
{
struct jsonrpc_msg *reply;
if (!strcmp(request->method, "transact")) {
reply = execute_transaction(s, request);
} else if (!strcmp(request->method, "get_schema")) {
reply = jsonrpc_create_reply(
ovsdb_schema_to_json(s->server->db->schema), request->id);
} else {
reply = jsonrpc_create_error(json_string_create("unknown method"),
request->id);
}
if (reply) {
jsonrpc_msg_destroy(request);
jsonrpc_send(s->rpc, reply);
}
}
static void
execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request)
{
size_t hash = json_hash(request->id, 0);
struct ovsdb_jsonrpc_trigger *t;
t = ovsdb_jsonrpc_trigger_find(s, request->params, hash);
if (t) {
ovsdb_jsonrpc_trigger_complete(t);
}
}
static void
ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *s,
struct jsonrpc_msg *request)
{
if (!strcmp(request->method, "cancel")) {
execute_cancel(s, request);
}
jsonrpc_msg_destroy(request);
}

29
ovsdb/jsonrpc-server.h Normal file
View File

@@ -0,0 +1,29 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_JSONRPC_SERVER_H
#define OVSDB_JSONRPC_SERVER_H 1
struct ovsdb;
struct ovsdb_jsonrpc_server;
struct svec;
int ovsdb_jsonrpc_server_create(struct ovsdb *, const struct svec *active,
const struct svec *passive,
struct ovsdb_jsonrpc_server **);
void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *);
void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *);
#endif /* ovsdb/jsonrpc-server.h */

223
ovsdb/ovsdb-server.c Normal file
View File

@@ -0,0 +1,223 @@
/* Copyright (c) 2009 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.h"
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include "command-line.h"
#include "daemon.h"
#include "fault.h"
#include "json.h"
#include "jsonrpc.h"
#include "jsonrpc-server.h"
#include "leak-checker.h"
#include "list.h"
#include "ovsdb-error.h"
#include "poll-loop.h"
#include "process.h"
#include "stream.h"
#include "svec.h"
#include "timeval.h"
#include "trigger.h"
#include "util.h"
#include "unixctl.h"
#include "vlog.h"
#define THIS_MODULE VLM_ovsdb_server
static const struct jsonrpc_server_cbs ovsdb_jsonrpc_cbs;
static void parse_options(int argc, char *argv[], char **file_namep,
struct svec *active, struct svec *passive);
static void usage(void) NO_RETURN;
static void ovsdb_transact(struct unixctl_conn *, const char *args, void *db);
int
main(int argc, char *argv[])
{
struct unixctl_server *unixctl;
struct ovsdb_jsonrpc_server *jsonrpc;
struct svec active, passive;
struct ovsdb_error *error;
struct ovsdb *db;
char *file_name;
int retval;
set_program_name(argv[0]);
register_fault_handlers();
time_init();
vlog_init();
signal(SIGPIPE, SIG_IGN);
process_init();
parse_options(argc, argv, &file_name, &active, &passive);
error = ovsdb_open(file_name, false, &db);
if (error) {
ovs_fatal(0, "%s", ovsdb_error_to_string(error));
}
retval = ovsdb_jsonrpc_server_create(db, &active, &passive, &jsonrpc);
if (retval) {
ovs_fatal(retval, "failed to initialize JSON-RPC server for OVSDB");
}
svec_destroy(&active);
svec_destroy(&passive);
die_if_already_running();
daemonize();
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
ovs_fatal(retval, "could not listen for control connections");
}
unixctl_command_register("ovsdb/transact", ovsdb_transact, db);
for (;;) {
ovsdb_jsonrpc_server_run(jsonrpc);
unixctl_server_run(unixctl);
ovsdb_trigger_run(db, time_msec());
ovsdb_jsonrpc_server_wait(jsonrpc);
unixctl_server_wait(unixctl);
ovsdb_trigger_wait(db, time_msec());
poll_block();
}
return 0;
}
static void
parse_options(int argc, char *argv[], char **file_namep,
struct svec *active, struct svec *passive)
{
enum {
OPT_DUMMY = UCHAR_MAX + 1,
OPT_CONNECT,
OPT_LISTEN,
VLOG_OPTION_ENUMS,
LEAK_CHECKER_OPTION_ENUMS
};
static struct option long_options[] = {
{"connect", required_argument, 0, OPT_CONNECT},
{"listen", required_argument, 0, OPT_LISTEN},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
LEAK_CHECKER_LONG_OPTIONS,
{0, 0, 0, 0},
};
char *short_options = long_options_to_short_options(long_options);
svec_init(active);
svec_init(passive);
for (;;) {
int c;
c = getopt_long(argc, argv, short_options, long_options, NULL);
if (c == -1) {
break;
}
switch (c) {
case OPT_CONNECT:
svec_add(active, optarg);
break;
case OPT_LISTEN:
svec_add(passive, optarg);
break;
case 'h':
usage();
case 'V':
OVS_PRINT_VERSION(0, 0);
exit(EXIT_SUCCESS);
VLOG_OPTION_HANDLERS
DAEMON_OPTION_HANDLERS
LEAK_CHECKER_OPTION_HANDLERS
case '?':
exit(EXIT_FAILURE);
default:
abort();
}
}
free(short_options);
argc -= optind;
argv += optind;
if (argc != 1) {
ovs_fatal(0, "database file is only non-option argument; "
"use --help for usage");
}
*file_namep = argv[0];
}
static void
usage(void)
{
printf("%s: Open vSwitch database server\n"
"usage: %s [OPTIONS] DATABASE\n"
"where DATABASE is a database file in ovsdb format.\n",
program_name, program_name);
printf("\nJSON-RPC options (may be specified any number of times):\n"
" --connect=REMOTE make active connection to REMOTE\n"
" --listen=LOCAL passively listen on LOCAL\n");
stream_usage("JSON-RPC", true, true);
daemon_usage();
vlog_usage();
printf("\nOther options:\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
leak_checker_usage();
exit(EXIT_SUCCESS);
}
static void
ovsdb_transact(struct unixctl_conn *conn, const char *args, void *db_)
{
struct ovsdb *db = db_;
struct json *request, *reply;
char *reply_string;
/* Parse JSON. */
request = json_from_string(args);
if (request->type == JSON_STRING) {
unixctl_command_reply(conn, 501, request->u.string);
json_destroy(request);
return;
}
/* Execute command. */
reply = ovsdb_execute(db, request, 0, NULL);
reply_string = json_to_string(reply, 0);
unixctl_command_reply(conn, 200, reply_string);
free(reply_string);
json_destroy(reply);
}

204
ovsdb/ovsdb-tool.c Normal file
View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2009 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 <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include "command-line.h"
#include "compiler.h"
#include "file.h"
#include "json.h"
#include "ovsdb.h"
#include "ovsdb-error.h"
#include "table.h"
#include "timeval.h"
#include "util.h"
#include "vlog.h"
#define THIS_MODULE VLM_ovsdb_tool
static const struct command all_commands[];
static void usage(void) NO_RETURN;
static void parse_options(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
set_program_name(argv[0]);
time_init();
vlog_init();
parse_options(argc, argv);
signal(SIGPIPE, SIG_IGN);
run_command(argc - optind, argv + optind, all_commands);
return 0;
}
static void
parse_options(int argc, char *argv[])
{
static struct option long_options[] = {
{"verbose", optional_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0},
};
char *short_options = long_options_to_short_options(long_options);
for (;;) {
int c;
c = getopt_long(argc, argv, short_options, long_options, NULL);
if (c == -1) {
break;
}
switch (c) {
case 'h':
usage();
case 'V':
OVS_PRINT_VERSION(0, 0);
exit(EXIT_SUCCESS);
case 'v':
vlog_set_verbosity(optarg);
break;
case '?':
exit(EXIT_FAILURE);
default:
abort();
}
}
free(short_options);
}
static void
usage(void)
{
printf("%s: Open vSwitch database management utility\n"
"usage: %s [OPTIONS] COMMAND [ARG...]\n"
" create DB SCHEMA create DB with the given SCHEMA\n"
" compact DB [DST] compact DB in-place (or to DST)\n"
" extract-schema DB print DB's schema on stdout\n"
" query DB TRNS execute read-only transaction on DB\n"
" transact DB TRNS execute read/write transaction on DB\n",
program_name, program_name);
vlog_usage();
printf("\nOther options:\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
exit(EXIT_SUCCESS);
}
static struct json *
parse_json(const char *s)
{
struct json *json = json_from_string(s);
if (json->type == JSON_STRING) {
ovs_fatal(0, "\"%s\": %s", s, json->u.string);
}
return json;
}
static void
print_and_free_json(struct json *json)
{
char *string = json_to_string(json, JSSF_SORT);
json_destroy(json);
puts(string);
free(string);
}
static void
check_ovsdb_error(struct ovsdb_error *error)
{
if (error) {
ovs_fatal(0, "%s", ovsdb_error_to_string(error));
}
}
static void
do_create(int argc UNUSED, char *argv[])
{
const char *db_file_name = argv[1];
const char *schema_file_name = argv[2];
struct ovsdb_schema *schema;
struct ovsdb_file *db_file;
struct json *json;
/* Read schema from file and convert to JSON. */
check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
json = ovsdb_schema_to_json(schema);
/* Create database file. */
check_ovsdb_error(ovsdb_file_open(db_file_name, O_RDWR | O_CREAT | O_EXCL,
&db_file));
check_ovsdb_error(ovsdb_file_write(db_file, json));
check_ovsdb_error(ovsdb_file_commit(db_file));
ovsdb_file_close(db_file);
json_destroy(json);
}
static void
transact(int flags, const char *db_file_name, const char *transaction)
{
struct json *request, *result;
struct ovsdb *db;
check_ovsdb_error(ovsdb_open(db_file_name, flags, &db));
request = parse_json(transaction);
result = ovsdb_execute(db, request, 0, NULL);
json_destroy(request);
print_and_free_json(result);
ovsdb_destroy(db);
}
static void
do_query(int argc UNUSED, char *argv[])
{
transact(O_RDONLY, argv[1], argv[2]);
}
static void
do_transact(int argc UNUSED, char *argv[])
{
transact(O_RDWR, argv[1], argv[2]);
}
static void
do_help(int argc UNUSED, char *argv[] UNUSED)
{
usage();
}
static const struct command all_commands[] = {
{ "create", 2, 2, do_create },
{ "query", 2, 2, do_query },
{ "transact", 2, 2, do_transact },
{ "help", 0, INT_MAX, do_help },
{ NULL, 0, 0, NULL },
};

262
ovsdb/ovsdb.c Normal file
View File

@@ -0,0 +1,262 @@
/* Copyright (c) 2009 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.h"
#include <fcntl.h>
#include "file.h"
#include "json.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "table.h"
#include "transaction.h"
#define THIS_MODULE VLM_ovsdb
#include "vlog.h"
struct ovsdb_schema *
ovsdb_schema_create(const char *name, const char *comment)
{
struct ovsdb_schema *schema;
schema = xzalloc(sizeof *schema);
schema->name = xstrdup(name);
schema->comment = comment ? xstrdup(comment) : NULL;
shash_init(&schema->tables);
return schema;
}
void
ovsdb_schema_destroy(struct ovsdb_schema *schema)
{
struct shash_node *node;
SHASH_FOR_EACH (node, &schema->tables) {
ovsdb_table_schema_destroy(node->data);
}
shash_destroy(&schema->tables);
free(schema->comment);
free(schema->name);
free(schema);
}
struct ovsdb_error *
ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap)
{
struct ovsdb_schema *schema;
struct ovsdb_error *error;
struct json *json;
*schemap = NULL;
json = json_from_file(file_name);
if (json->type == JSON_STRING) {
error = ovsdb_error("failed to read schema",
"\"%s\" could not be read as JSON (%s)",
file_name, json_string(json));
json_destroy(json);
return error;
}
error = ovsdb_schema_from_json(json, &schema);
if (error) {
json_destroy(json);
return ovsdb_wrap_error(error,
"failed to parse \"%s\" as ovsdb schema",
file_name);
}
*schemap = schema;
return NULL;
}
struct ovsdb_error *
ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
{
struct ovsdb_schema *schema;
const struct json *name, *comment, *tables;
struct ovsdb_error *error;
struct shash_node *node;
struct ovsdb_parser parser;
*schemap = NULL;
ovsdb_parser_init(&parser, json, "database schema");
name = ovsdb_parser_member(&parser, "name", OP_ID);
comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL);
tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
error = ovsdb_parser_finish(&parser);
if (error) {
return error;
}
schema = ovsdb_schema_create(json_string(name),
comment ? json_string(comment) : NULL);
SHASH_FOR_EACH (node, json_object(tables)) {
struct ovsdb_table_schema *table;
if (node->name[0] == '_') {
error = ovsdb_syntax_error(json, NULL, "names beginning with "
"\"_\" are reserved");
} else {
error = ovsdb_table_schema_from_json(node->data, node->name,
&table);
}
if (error) {
ovsdb_schema_destroy(schema);
return error;
}
shash_add(&schema->tables, table->name, table);
}
*schemap = schema;
return 0;
}
struct json *
ovsdb_schema_to_json(const struct ovsdb_schema *schema)
{
struct json *json, *tables;
struct shash_node *node;
json = json_object_create();
json_object_put_string(json, "name", schema->name);
if (schema->comment) {
json_object_put_string(json, "comment", schema->comment);
}
tables = json_object_create();
SHASH_FOR_EACH (node, &schema->tables) {
struct ovsdb_table_schema *table = node->data;
json_object_put(tables, table->name,
ovsdb_table_schema_to_json(table));
}
json_object_put(json, "tables", tables);
return json;
}
struct ovsdb *
ovsdb_create(struct ovsdb_file *file, struct ovsdb_schema *schema)
{
struct shash_node *node;
struct ovsdb *db;
db = xmalloc(sizeof *db);
db->schema = schema;
db->file = file;
list_init(&db->triggers);
db->run_triggers = false;
shash_init(&db->tables);
SHASH_FOR_EACH (node, &schema->tables) {
struct ovsdb_table_schema *ts = node->data;
shash_add(&db->tables, node->name, ovsdb_table_create(ts));
}
return db;
}
struct ovsdb_error *
ovsdb_open(const char *file_name, bool read_only, struct ovsdb **dbp)
{
struct ovsdb_schema *schema;
struct ovsdb_error *error;
struct ovsdb_file *file;
struct json *json;
struct ovsdb *db;
error = ovsdb_file_open(file_name, read_only ? O_RDONLY : O_RDWR, &file);
if (error) {
return error;
}
error = ovsdb_file_read(file, &json);
if (error) {
return error;
} else if (!json) {
return ovsdb_io_error(EOF, "%s: database file contains no schema",
file_name);
}
error = ovsdb_schema_from_json(json, &schema);
if (error) {
json_destroy(json);
return ovsdb_wrap_error(error,
"failed to parse \"%s\" as ovsdb schema",
file_name);
}
json_destroy(json);
db = ovsdb_create(read_only ? file : NULL, schema);
while ((error = ovsdb_file_read(file, &json)) == NULL && json) {
struct ovsdb_txn *txn;
error = ovsdb_txn_from_json(db, json, &txn);
json_destroy(json);
if (error) {
break;
}
ovsdb_txn_commit(txn);
}
if (error) {
char *msg = ovsdb_error_to_string(error);
VLOG_WARN("%s", msg);
free(msg);
ovsdb_error_destroy(error);
}
if (read_only) {
ovsdb_file_close(file);
}
*dbp = db;
return NULL;
}
void
ovsdb_destroy(struct ovsdb *db)
{
if (db) {
struct shash_node *node;
/* Delete all the tables. This also deletes their schemas. */
SHASH_FOR_EACH (node, &db->tables) {
struct ovsdb_table *table = node->data;
ovsdb_table_destroy(table);
}
shash_destroy(&db->tables);
/* Clear the schema's hash of table schemas. The schemas, but not the
* table that points to them, were deleted in the previous step. */
shash_destroy(&db->schema->tables);
ovsdb_schema_destroy(db->schema);
ovsdb_file_close(db->file);
free(db);
}
}
struct ovsdb_table *
ovsdb_get_table(const struct ovsdb *db, const char *name)
{
return shash_find_data(&db->tables, name);
}

73
ovsdb/ovsdb.h Normal file
View File

@@ -0,0 +1,73 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_OVSDB_H
#define OVSDB_OVSDB_H 1
#include "compiler.h"
#include "hmap.h"
#include "list.h"
#include "shash.h"
struct json;
struct uuid;
/* Database schema. */
struct ovsdb_schema {
char *name;
char *comment;
struct shash tables; /* Contains "struct ovsdb_table_schema *"s. */
};
struct ovsdb_schema *ovsdb_schema_create(const char *name,
const char *comment);
void ovsdb_schema_destroy(struct ovsdb_schema *);
struct ovsdb_error *ovsdb_schema_from_file(const char *file_name,
struct ovsdb_schema **)
WARN_UNUSED_RESULT;
struct ovsdb_error *ovsdb_schema_from_json(struct json *,
struct ovsdb_schema **)
WARN_UNUSED_RESULT;
struct json *ovsdb_schema_to_json(const struct ovsdb_schema *);
/* Database. */
struct ovsdb {
struct ovsdb_schema *schema;
struct ovsdb_file *file; /* Disk file (null for in-memory db). */
struct shash tables; /* Contains "struct ovsdb_table *"s. */
/* Triggers. */
struct list triggers; /* Contains "struct ovsdb_trigger"s. */
bool run_triggers;
};
struct ovsdb *ovsdb_create(struct ovsdb_file *, struct ovsdb_schema *);
struct ovsdb_error *ovsdb_open(const char *file_name, bool read_only,
struct ovsdb **)
WARN_UNUSED_RESULT;
void ovsdb_destroy(struct ovsdb *);
struct ovsdb_error *ovsdb_from_json(const struct json *, struct ovsdb **)
WARN_UNUSED_RESULT;
struct json *ovsdb_to_json(const struct ovsdb *);
struct ovsdb_table *ovsdb_get_table(const struct ovsdb *, const char *);
struct json *ovsdb_execute(struct ovsdb *, const struct json *params,
long long int elapsed_msec,
long long int *timeout_msec);
#endif /* ovsdb/ovsdb.h */

99
ovsdb/query.c Normal file
View File

@@ -0,0 +1,99 @@
/* Copyright (c) 2009 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 "query.h"
#include "column.h"
#include "condition.h"
#include "row.h"
#include "table.h"
void
ovsdb_query(struct ovsdb_table *table, const struct ovsdb_condition *cnd,
bool (*output_row)(const struct ovsdb_row *, void *aux), void *aux)
{
if (cnd->n_clauses > 0
&& cnd->clauses[0].column->index == OVSDB_COL_UUID
&& cnd->clauses[0].function == OVSDB_F_EQ) {
/* Optimize the case where the query has a clause of the form "uuid ==
* <some-uuid>", since we have an index on UUID. */
const struct ovsdb_row *row;
row = ovsdb_table_get_row(table, &cnd->clauses[0].arg.keys[0].uuid);
if (row && row->table == table && ovsdb_condition_evaluate(row, cnd)) {
output_row(row, aux);
}
} else {
/* Linear scan. */
const struct ovsdb_row *row, *next;
HMAP_FOR_EACH_SAFE (row, next, struct ovsdb_row, hmap_node,
&table->rows) {
if (ovsdb_condition_evaluate(row, cnd) && !output_row(row, aux)) {
break;
}
}
}
}
static bool
query_row_set_cb(const struct ovsdb_row *row, void *results_)
{
struct ovsdb_row_set *results = results_;
ovsdb_row_set_add_row(results, row);
return true;
}
void
ovsdb_query_row_set(struct ovsdb_table *table,
const struct ovsdb_condition *condition,
struct ovsdb_row_set *results)
{
ovsdb_query(table, condition, query_row_set_cb, results);
}
static bool
query_distinct_cb(const struct ovsdb_row *row, void *hash_)
{
struct ovsdb_row_hash *hash = hash_;
ovsdb_row_hash_insert(hash, row);
return true;
}
void
ovsdb_query_distinct(struct ovsdb_table *table,
const struct ovsdb_condition *condition,
const struct ovsdb_column_set *columns,
struct ovsdb_row_set *results)
{
if (!columns || ovsdb_column_set_contains(columns, OVSDB_COL_UUID)) {
/* All the result rows are guaranteed to be distinct anyway. */
return ovsdb_query_row_set(table, condition, results);
} else {
/* Use hash table to drop duplicates. */
struct ovsdb_row_hash_node *node;
struct ovsdb_row_hash hash;
ovsdb_row_hash_init(&hash, columns);
ovsdb_query(table, condition, query_distinct_cb, &hash);
HMAP_FOR_EACH (node, struct ovsdb_row_hash_node, hmap_node,
&hash.rows) {
ovsdb_row_set_add_row(results, node->row);
}
ovsdb_row_hash_destroy(&hash, false);
}
}

37
ovsdb/query.h Normal file
View File

@@ -0,0 +1,37 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_QUERY_H
#define OVSDB_QUERY_H 1
#include <stdbool.h>
struct ovsdb_column_set;
struct ovsdb_condition;
struct ovsdb_row;
struct ovsdb_row_set;
struct ovsdb_table;
struct ovsdb_txn;
void ovsdb_query(struct ovsdb_table *, const struct ovsdb_condition *,
bool (*output_row)(const struct ovsdb_row *, void *aux),
void *aux);
void ovsdb_query_row_set(struct ovsdb_table *, const struct ovsdb_condition *,
struct ovsdb_row_set *);
void ovsdb_query_distinct(struct ovsdb_table *, const struct ovsdb_condition *,
const struct ovsdb_column_set *,
struct ovsdb_row_set *);
#endif /* ovsdb/query.h */

386
ovsdb/row.c Normal file
View File

@@ -0,0 +1,386 @@
/* Copyright (c) 2009 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 "row.h"
#include <assert.h>
#include <stddef.h>
#include "json.h"
#include "ovsdb-error.h"
#include "shash.h"
#include "sort.h"
#include "table.h"
static struct ovsdb_row *
allocate_row(const struct ovsdb_table *table)
{
size_t n_fields = shash_count(&table->schema->columns);
size_t row_size = (offsetof(struct ovsdb_row, fields)
+ sizeof(struct ovsdb_datum) * n_fields);
struct ovsdb_row *row = xmalloc(row_size);
row->table = (struct ovsdb_table *) table;
row->txn_row = NULL;
return row;
}
struct ovsdb_row *
ovsdb_row_create(const struct ovsdb_table *table)
{
struct shash_node *node;
struct ovsdb_row *row;
row = allocate_row(table);
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_datum_init_default(&row->fields[column->index], &column->type);
}
return row;
}
struct ovsdb_row *
ovsdb_row_clone(const struct ovsdb_row *old)
{
const struct ovsdb_table *table = old->table;
const struct shash_node *node;
struct ovsdb_row *new;
new = allocate_row(table);
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_datum_clone(&new->fields[column->index],
&old->fields[column->index],
&column->type);
}
return new;
}
/* The caller is responsible for ensuring that 'row' has been removed from its
* table and that it is not participating in a transaction. */
void
ovsdb_row_destroy(struct ovsdb_row *row)
{
if (row) {
const struct ovsdb_table *table = row->table;
const struct shash_node *node;
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_datum_destroy(&row->fields[column->index], &column->type);
}
free(row);
}
}
uint32_t
ovsdb_row_hash_columns(const struct ovsdb_row *row,
const struct ovsdb_column_set *columns,
uint32_t basis)
{
size_t i;
for (i = 0; i < columns->n_columns; i++) {
const struct ovsdb_column *column = columns->columns[i];
basis = ovsdb_datum_hash(&row->fields[column->index], &column->type,
basis);
}
return basis;
}
int
ovsdb_row_compare_columns_3way(const struct ovsdb_row *a,
const struct ovsdb_row *b,
const struct ovsdb_column_set *columns)
{
size_t i;
for (i = 0; i < columns->n_columns; i++) {
const struct ovsdb_column *column = columns->columns[i];
int cmp = ovsdb_datum_compare_3way(&a->fields[column->index],
&b->fields[column->index],
&column->type);
if (cmp) {
return cmp;
}
}
return 0;
}
bool
ovsdb_row_equal_columns(const struct ovsdb_row *a,
const struct ovsdb_row *b,
const struct ovsdb_column_set *columns)
{
size_t i;
for (i = 0; i < columns->n_columns; i++) {
const struct ovsdb_column *column = columns->columns[i];
if (!ovsdb_datum_equals(&a->fields[column->index],
&b->fields[column->index],
&column->type)) {
return false;
}
}
return true;
}
void
ovsdb_row_update_columns(struct ovsdb_row *dst,
const struct ovsdb_row *src,
const struct ovsdb_column_set *columns)
{
size_t i;
for (i = 0; i < columns->n_columns; i++) {
const struct ovsdb_column *column = columns->columns[i];
ovsdb_datum_destroy(&dst->fields[column->index], &column->type);
ovsdb_datum_clone(&dst->fields[column->index],
&src->fields[column->index],
&column->type);
}
}
struct ovsdb_error *
ovsdb_row_from_json(struct ovsdb_row *row, const struct json *json,
const struct ovsdb_symbol_table *symtab,
struct ovsdb_column_set *included)
{
struct ovsdb_table_schema *schema = row->table->schema;
struct ovsdb_error *error;
struct shash_node *node;
if (json->type != JSON_OBJECT) {
return ovsdb_syntax_error(json, NULL, "row must be JSON object");
}
SHASH_FOR_EACH (node, json_object(json)) {
const char *column_name = node->name;
const struct ovsdb_column *column;
struct ovsdb_datum datum;
column = ovsdb_table_schema_get_column(schema, column_name);
if (!column) {
return ovsdb_syntax_error(json, "unknown column",
"No column %s in table %s.",
column_name, schema->name);
}
error = ovsdb_datum_from_json(&datum, &column->type, node->data,
symtab);
if (error) {
return error;
}
ovsdb_datum_swap(&row->fields[column->index], &datum);
ovsdb_datum_destroy(&datum, &column->type);
if (included) {
ovsdb_column_set_add(included, column);
}
}
return NULL;
}
static void
put_json_column(struct json *object, const struct ovsdb_row *row,
const struct ovsdb_column *column)
{
json_object_put(object, column->name,
ovsdb_datum_to_json(&row->fields[column->index],
&column->type));
}
struct json *
ovsdb_row_to_json(const struct ovsdb_row *row,
const struct ovsdb_column_set *columns)
{
struct json *json;
size_t i;
json = json_object_create();
for (i = 0; i < columns->n_columns; i++) {
put_json_column(json, row, columns->columns[i]);
}
return json;
}
void
ovsdb_row_set_init(struct ovsdb_row_set *set)
{
set->rows = NULL;
set->n_rows = set->allocated_rows = 0;
}
void
ovsdb_row_set_destroy(struct ovsdb_row_set *set)
{
free(set->rows);
}
void
ovsdb_row_set_add_row(struct ovsdb_row_set *set, const struct ovsdb_row *row)
{
if (set->n_rows >= set->allocated_rows) {
set->rows = x2nrealloc(set->rows, &set->allocated_rows,
sizeof *set->rows);
}
set->rows[set->n_rows++] = row;
}
struct json *
ovsdb_row_set_to_json(const struct ovsdb_row_set *rows,
const struct ovsdb_column_set *columns)
{
struct json **json_rows;
size_t i;
json_rows = xmalloc(rows->n_rows * sizeof *json_rows);
for (i = 0; i < rows->n_rows; i++) {
json_rows[i] = ovsdb_row_to_json(rows->rows[i], columns);
}
return json_array_create(json_rows, rows->n_rows);
}
struct ovsdb_row_set_sort_cbdata {
struct ovsdb_row_set *set;
const struct ovsdb_column_set *columns;
};
static int
ovsdb_row_set_sort_compare_cb(size_t a, size_t b, void *cbdata_)
{
struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_;
return ovsdb_row_compare_columns_3way(cbdata->set->rows[a],
cbdata->set->rows[b],
cbdata->columns);
}
static void
ovsdb_row_set_sort_swap_cb(size_t a, size_t b, void *cbdata_)
{
struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_;
const struct ovsdb_row *tmp = cbdata->set->rows[a];
cbdata->set->rows[a] = cbdata->set->rows[b];
cbdata->set->rows[b] = tmp;
}
void
ovsdb_row_set_sort(struct ovsdb_row_set *set,
const struct ovsdb_column_set *columns)
{
if (columns && columns->n_columns && set->n_rows > 1) {
struct ovsdb_row_set_sort_cbdata cbdata;
cbdata.set = set;
cbdata.columns = columns;
sort(set->n_rows,
ovsdb_row_set_sort_compare_cb,
ovsdb_row_set_sort_swap_cb,
&cbdata);
}
}
void
ovsdb_row_hash_init(struct ovsdb_row_hash *rh,
const struct ovsdb_column_set *columns)
{
hmap_init(&rh->rows);
ovsdb_column_set_clone(&rh->columns, columns);
}
void
ovsdb_row_hash_destroy(struct ovsdb_row_hash *rh, bool destroy_rows)
{
struct ovsdb_row_hash_node *node, *next;
HMAP_FOR_EACH_SAFE (node, next, struct ovsdb_row_hash_node, hmap_node,
&rh->rows) {
hmap_remove(&rh->rows, &node->hmap_node);
if (destroy_rows) {
ovsdb_row_destroy((struct ovsdb_row *) node->row);
}
free(node);
}
hmap_destroy(&rh->rows);
ovsdb_column_set_destroy(&rh->columns);
}
size_t
ovsdb_row_hash_count(const struct ovsdb_row_hash *rh)
{
return hmap_count(&rh->rows);
}
bool
ovsdb_row_hash_contains(const struct ovsdb_row_hash *rh,
const struct ovsdb_row *row)
{
size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0);
return ovsdb_row_hash_contains__(rh, row, hash);
}
/* Returns true if every row in 'b' has an equal row in 'a'. */
bool
ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *a,
const struct ovsdb_row_hash *b)
{
struct ovsdb_row_hash_node *node;
assert(ovsdb_column_set_equals(&a->columns, &b->columns));
HMAP_FOR_EACH (node, struct ovsdb_row_hash_node, hmap_node, &b->rows) {
if (!ovsdb_row_hash_contains__(a, node->row, node->hmap_node.hash)) {
return false;
}
}
return true;
}
bool
ovsdb_row_hash_insert(struct ovsdb_row_hash *rh, const struct ovsdb_row *row)
{
size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0);
return ovsdb_row_hash_insert__(rh, row, hash);
}
bool
ovsdb_row_hash_contains__(const struct ovsdb_row_hash *rh,
const struct ovsdb_row *row, size_t hash)
{
struct ovsdb_row_hash_node *node;
HMAP_FOR_EACH_WITH_HASH (node, struct ovsdb_row_hash_node, hmap_node,
hash, &rh->rows) {
if (ovsdb_row_equal_columns(row, node->row, &rh->columns)) {
return true;
}
}
return false;
}
bool
ovsdb_row_hash_insert__(struct ovsdb_row_hash *rh, const struct ovsdb_row *row,
size_t hash)
{
if (!ovsdb_row_hash_contains__(rh, row, hash)) {
struct ovsdb_row_hash_node *node = xmalloc(sizeof *node);
node->row = row;
hmap_insert(&rh->rows, &node->hmap_node, hash);
return true;
} else {
return false;
}
}

139
ovsdb/row.h Normal file
View File

@@ -0,0 +1,139 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_ROW_H
#define OVSDB_ROW_H 1
#include <stddef.h>
#include <stdint.h>
#include "column.h"
#include "hmap.h"
#include "ovsdb-data.h"
struct ovsdb_column_set;
/* A row in a database table. */
struct ovsdb_row {
struct ovsdb_table *table; /* Table to which this belongs. */
struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */
struct ovsdb_txn_row *txn_row; /* Transaction that row is in, if any. */
struct ovsdb_datum fields[];
};
struct ovsdb_row *ovsdb_row_create(const struct ovsdb_table *);
struct ovsdb_row *ovsdb_row_clone(const struct ovsdb_row *);
void ovsdb_row_destroy(struct ovsdb_row *);
uint32_t ovsdb_row_hash_columns(const struct ovsdb_row *,
const struct ovsdb_column_set *,
uint32_t basis);
bool ovsdb_row_equal_columns(const struct ovsdb_row *,
const struct ovsdb_row *,
const struct ovsdb_column_set *);
int ovsdb_row_compare_columns_3way(const struct ovsdb_row *,
const struct ovsdb_row *,
const struct ovsdb_column_set *);
void ovsdb_row_update_columns(struct ovsdb_row *, const struct ovsdb_row *,
const struct ovsdb_column_set *);
struct ovsdb_error *ovsdb_row_from_json(struct ovsdb_row *,
const struct json *,
const struct ovsdb_symbol_table *,
struct ovsdb_column_set *included)
WARN_UNUSED_RESULT;
struct json *ovsdb_row_to_json(const struct ovsdb_row *,
const struct ovsdb_column_set *include);
static inline const struct uuid *
ovsdb_row_get_uuid(const struct ovsdb_row *row)
{
return &row->fields[OVSDB_COL_UUID].keys[0].uuid;
}
static inline struct uuid *
ovsdb_row_get_uuid_rw(struct ovsdb_row *row)
{
return &row->fields[OVSDB_COL_UUID].keys[0].uuid;
}
static inline const struct uuid *
ovsdb_row_get_version(const struct ovsdb_row *row)
{
return &row->fields[OVSDB_COL_VERSION].keys[0].uuid;
}
static inline struct uuid *
ovsdb_row_get_version_rw(struct ovsdb_row *row)
{
return &row->fields[OVSDB_COL_VERSION].keys[0].uuid;
}
static inline uint32_t
ovsdb_row_hash(const struct ovsdb_row *row)
{
return uuid_hash(ovsdb_row_get_uuid(row));
}
/* An unordered collection of rows. */
struct ovsdb_row_set {
const struct ovsdb_row **rows;
size_t n_rows, allocated_rows;
};
#define OVSDB_ROW_SET_INITIALIZER { NULL, 0, 0 }
void ovsdb_row_set_init(struct ovsdb_row_set *);
void ovsdb_row_set_destroy(struct ovsdb_row_set *);
void ovsdb_row_set_add_row(struct ovsdb_row_set *, const struct ovsdb_row *);
struct json *ovsdb_row_set_to_json(const struct ovsdb_row_set *,
const struct ovsdb_column_set *);
void ovsdb_row_set_sort(struct ovsdb_row_set *,
const struct ovsdb_column_set *);
/* A hash table of rows. A specified set of columns is used for hashing and
* comparing rows.
*
* The row hash doesn't necessarily own its rows. They may be owned by, for
* example, an ovsdb_table. */
struct ovsdb_row_hash {
struct hmap rows;
struct ovsdb_column_set columns;
};
#define OVSDB_ROW_HASH_INITIALIZER(RH) \
{ HMAP_INITIALIZER(&(RH).rows), OVSDB_COLUMN_SET_INITIALIZER }
struct ovsdb_row_hash_node {
struct hmap_node hmap_node;
const struct ovsdb_row *row;
};
void ovsdb_row_hash_init(struct ovsdb_row_hash *,
const struct ovsdb_column_set *);
void ovsdb_row_hash_destroy(struct ovsdb_row_hash *, bool destroy_rows);
size_t ovsdb_row_hash_count(const struct ovsdb_row_hash *);
bool ovsdb_row_hash_contains(const struct ovsdb_row_hash *,
const struct ovsdb_row *);
bool ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *,
const struct ovsdb_row_hash *);
bool ovsdb_row_hash_insert(struct ovsdb_row_hash *, const struct ovsdb_row *);
bool ovsdb_row_hash_contains__(const struct ovsdb_row_hash *,
const struct ovsdb_row *, size_t hash);
bool ovsdb_row_hash_insert__(struct ovsdb_row_hash *,
const struct ovsdb_row *, size_t hash);
#endif /* ovsdb/row.h */

228
ovsdb/table.c Normal file
View File

@@ -0,0 +1,228 @@
/* Copyright (c) 2009 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 "table.h"
#include <assert.h>
#include "json.h"
#include "column.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "ovsdb-types.h"
#include "row.h"
static void
add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column)
{
assert(!shash_find(&ts->columns, column->name));
column->index = shash_count(&ts->columns);
shash_add(&ts->columns, column->name, column);
}
struct ovsdb_table_schema *
ovsdb_table_schema_create(const char *name, const char *comment, bool mutable)
{
struct ovsdb_column *uuid, *version;
struct ovsdb_table_schema *ts;
ts = xzalloc(sizeof *ts);
ts->name = xstrdup(name);
ts->comment = comment ? xstrdup(comment) : NULL;
ts->mutable = mutable;
shash_init(&ts->columns);
uuid = ovsdb_column_create(
"_uuid", "Unique identifier for this row.",
false, true, &ovsdb_type_uuid);
add_column(ts, uuid);
assert(uuid->index == OVSDB_COL_UUID);
version = ovsdb_column_create(
"_version", "Unique identifier for this version of this row.",
false, false, &ovsdb_type_uuid);
add_column(ts, version);
assert(version->index == OVSDB_COL_VERSION);
return ts;
}
void
ovsdb_table_schema_destroy(struct ovsdb_table_schema *ts)
{
struct shash_node *node;
SHASH_FOR_EACH (node, &ts->columns) {
ovsdb_column_destroy(node->data);
}
shash_destroy(&ts->columns);
free(ts->comment);
free(ts->name);
free(ts);
}
struct ovsdb_error *
ovsdb_table_schema_from_json(const struct json *json, const char *name,
struct ovsdb_table_schema **tsp)
{
struct ovsdb_table_schema *ts;
const struct json *comment, *columns, *mutable;
struct shash_node *node;
struct ovsdb_parser parser;
struct ovsdb_error *error;
*tsp = NULL;
ovsdb_parser_init(&parser, json, "table schema for table %s", name);
comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL);
columns = ovsdb_parser_member(&parser, "columns", OP_OBJECT);
mutable = ovsdb_parser_member(&parser, "mutable",
OP_TRUE | OP_FALSE | OP_OPTIONAL);
error = ovsdb_parser_finish(&parser);
if (error) {
return error;
}
if (shash_is_empty(json_object(columns))) {
return ovsdb_syntax_error(json, NULL,
"table must have at least one column");
}
ts = ovsdb_table_schema_create(name,
comment ? json_string(comment) : NULL,
mutable ? json_boolean(mutable) : true);
SHASH_FOR_EACH (node, json_object(columns)) {
struct ovsdb_column *column;
if (node->name[0] == '_') {
error = ovsdb_syntax_error(json, NULL, "names beginning with "
"\"_\" are reserved");
} else {
error = ovsdb_column_from_json(node->data, node->name, &column);
}
if (error) {
ovsdb_table_schema_destroy(ts);
return error;
}
add_column(ts, column);
}
*tsp = ts;
return 0;
}
struct json *
ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts)
{
struct json *json, *columns;
struct shash_node *node;
json = json_object_create();
if (ts->comment) {
json_object_put_string(json, "comment", ts->comment);
}
if (!ts->mutable) {
json_object_put(json, "mutable", json_boolean_create(false));
}
columns = json_object_create();
SHASH_FOR_EACH (node, &ts->columns) {
struct ovsdb_column *column = node->data;
if (node->name[0] != '_') {
json_object_put(columns, column->name,
ovsdb_column_to_json(column));
}
}
json_object_put(json, "columns", columns);
return json;
}
const struct ovsdb_column *
ovsdb_table_schema_get_column(const struct ovsdb_table_schema *ts,
const char *name)
{
return shash_find_data(&ts->columns, name);
}
struct ovsdb_table *
ovsdb_table_create(struct ovsdb_table_schema *ts)
{
struct ovsdb_table *table;
table = xmalloc(sizeof *table);
table->schema = ts;
hmap_init(&table->rows);
return table;
}
void
ovsdb_table_destroy(struct ovsdb_table *table)
{
if (table) {
struct ovsdb_row *row, *next;
HMAP_FOR_EACH_SAFE (row, next, struct ovsdb_row, hmap_node,
&table->rows) {
ovsdb_row_destroy(row);
}
hmap_destroy(&table->rows);
ovsdb_table_schema_destroy(table->schema);
free(table);
}
}
static const struct ovsdb_row *
ovsdb_table_get_row__(const struct ovsdb_table *table, const struct uuid *uuid,
size_t hash)
{
struct ovsdb_row *row;
HMAP_FOR_EACH_WITH_HASH (row, struct ovsdb_row, hmap_node, hash,
&table->rows) {
if (uuid_equals(ovsdb_row_get_uuid(row), uuid)) {
return row;
}
}
return NULL;
}
const struct ovsdb_row *
ovsdb_table_get_row(const struct ovsdb_table *table, const struct uuid *uuid)
{
return ovsdb_table_get_row__(table, uuid, uuid_hash(uuid));
}
/* This is probably not the function you want. Use ovsdb_txn_row_modify()
* instead. */
bool
ovsdb_table_put_row(struct ovsdb_table *table, struct ovsdb_row *row)
{
const struct uuid *uuid = ovsdb_row_get_uuid(row);
size_t hash = uuid_hash(uuid);
if (!ovsdb_table_get_row__(table, uuid, hash)) {
hmap_insert(&table->rows, &row->hmap_node, hash);
return true;
} else {
return false;
}
}

63
ovsdb/table.h Normal file
View File

@@ -0,0 +1,63 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_TABLE_H
#define OVSDB_TABLE_H 1
#include <stdbool.h>
#include "compiler.h"
#include "hmap.h"
#include "shash.h"
struct json;
struct uuid;
/* Schema for a database table. */
struct ovsdb_table_schema {
char *name;
char *comment;
bool mutable;
struct shash columns; /* Contains "struct ovsdb_column *"s. */
};
struct ovsdb_table_schema *ovsdb_table_schema_create(const char *name,
const char *comment,
bool mutable);
void ovsdb_table_schema_destroy(struct ovsdb_table_schema *);
struct ovsdb_error *ovsdb_table_schema_from_json(const struct json *,
const char *name,
struct ovsdb_table_schema **)
WARN_UNUSED_RESULT;
struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *);
const struct ovsdb_column *ovsdb_table_schema_get_column(
const struct ovsdb_table_schema *, const char *name);
/* Database table. */
struct ovsdb_table {
struct ovsdb_table_schema *schema;
struct hmap rows; /* Contains "struct ovsdb_row"s. */
};
struct ovsdb_table *ovsdb_table_create(struct ovsdb_table_schema *);
void ovsdb_table_destroy(struct ovsdb_table *);
const struct ovsdb_row *ovsdb_table_get_row(const struct ovsdb_table *,
const struct uuid *);
bool ovsdb_table_put_row(struct ovsdb_table *, struct ovsdb_row *);
#endif /* ovsdb/table.h */

444
ovsdb/transaction.c Normal file
View File

@@ -0,0 +1,444 @@
/* Copyright (c) 2009 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 "transaction.h"
#include <assert.h>
#include "hash.h"
#include "hmap.h"
#include "json.h"
#include "ovsdb-error.h"
#include "ovsdb.h"
#include "row.h"
#include "table.h"
#include "uuid.h"
struct ovsdb_txn {
struct ovsdb *db;
struct hmap txn_tables; /* Contains "struct ovsdb_txn_table"s. */
};
/* A table modified by a transaction. */
struct ovsdb_txn_table {
struct hmap_node hmap_node; /* Element in ovsdb_txn's txn_tables hmap. */
struct ovsdb_table *table;
struct hmap txn_rows; /* Contains "struct ovsdb_txn_row"s. */
};
/* A row modified by the transaction:
*
* - A row added by a transaction will have null 'old' and non-null 'new'.
*
* - A row deleted by a transaction will have non-null 'old' and null
* 'new'.
*
* - A row modified by a transaction will have non-null 'old' and 'new'.
*
* - 'old' and 'new' both null is invalid. It would indicate that a row
* was added then deleted within a single transaction, but we instead
* handle that case by deleting the txn_row entirely.
*/
struct ovsdb_txn_row {
struct hmap_node hmap_node; /* In ovsdb_txn_table's txn_rows hmap. */
struct ovsdb_row *old; /* The old row. */
struct ovsdb_row *new; /* The new row. */
};
static const struct uuid *
ovsdb_txn_row_get_uuid(const struct ovsdb_txn_row *txn_row)
{
const struct ovsdb_row *row = txn_row->old ? txn_row->old : txn_row->new;
return ovsdb_row_get_uuid(row);
}
struct ovsdb_txn *
ovsdb_txn_create(struct ovsdb *db)
{
struct ovsdb_txn *txn = xmalloc(sizeof *txn);
txn->db = db;
hmap_init(&txn->txn_tables);
return txn;
}
static void
ovsdb_txn_destroy(struct ovsdb_txn *txn, void (*cb)(struct ovsdb_txn_row *))
{
struct ovsdb_txn_table *txn_table, *next_txn_table;
HMAP_FOR_EACH_SAFE (txn_table, next_txn_table,
struct ovsdb_txn_table, hmap_node, &txn->txn_tables)
{
struct ovsdb_txn_row *txn_row, *next_txn_row;
HMAP_FOR_EACH_SAFE (txn_row, next_txn_row,
struct ovsdb_txn_row, hmap_node,
&txn_table->txn_rows)
{
if (txn_row->new) {
txn_row->new->txn_row = NULL;
}
cb(txn_row);
free(txn_row);
}
hmap_destroy(&txn_table->txn_rows);
free(txn_table);
}
hmap_destroy(&txn->txn_tables);
free(txn);
}
static void
ovsdb_txn_row_abort(struct ovsdb_txn_row *txn_row)
{
struct ovsdb_row *old = txn_row->old;
struct ovsdb_row *new = txn_row->new;
if (!old) {
hmap_remove(&new->table->rows, &new->hmap_node);
} else if (!new) {
hmap_insert(&old->table->rows, &old->hmap_node, ovsdb_row_hash(old));
} else {
hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node);
}
ovsdb_row_destroy(new);
}
void
ovsdb_txn_abort(struct ovsdb_txn *txn)
{
ovsdb_txn_destroy(txn, ovsdb_txn_row_abort);
}
static void
ovsdb_txn_row_commit(struct ovsdb_txn_row *txn_row)
{
ovsdb_row_destroy(txn_row->old);
}
void
ovsdb_txn_commit(struct ovsdb_txn *txn)
{
txn->db->run_triggers = true;
ovsdb_txn_destroy(txn, ovsdb_txn_row_commit);
}
static void
put_json_column(struct json *object, const struct ovsdb_row *row,
const struct ovsdb_column *column)
{
json_object_put(object, column->name,
ovsdb_datum_to_json(&row->fields[column->index],
&column->type));
}
static struct json *
ovsdb_txn_row_to_json(const struct ovsdb_txn_row *txn_row)
{
const struct ovsdb_row *old = txn_row->old;
const struct ovsdb_row *new = txn_row->new;
struct shash_node *node;
struct json *json;
if (!new) {
return json_null_create();
}
json = NULL;
SHASH_FOR_EACH (node, &new->table->schema->columns) {
struct ovsdb_column *column = node->data;
unsigned int index = column->index;
if (index != OVSDB_COL_UUID && column->persistent
&& (!old || !ovsdb_datum_equals(&old->fields[index],
&new->fields[index],
&column->type)))
{
if (!json) {
json = json_object_create();
}
put_json_column(json, new, column);
}
}
return json;
}
static struct json *
ovsdb_txn_table_to_json(const struct ovsdb_txn_table *txn_table)
{
struct ovsdb_txn_row *txn_row;
struct json *txn_table_json;
txn_table_json = NULL;
HMAP_FOR_EACH (txn_row, struct ovsdb_txn_row, hmap_node,
&txn_table->txn_rows) {
struct json *txn_row_json = ovsdb_txn_row_to_json(txn_row);
if (txn_row_json) {
char uuid[UUID_LEN + 1];
if (!txn_table_json) {
txn_table_json = json_object_create();
}
snprintf(uuid, sizeof uuid,
UUID_FMT, UUID_ARGS(ovsdb_txn_row_get_uuid(txn_row)));
json_object_put(txn_table_json, uuid, txn_row_json);
}
}
return txn_table_json;
}
struct json *
ovsdb_txn_to_json(const struct ovsdb_txn *txn)
{
struct ovsdb_txn_table *txn_table;
struct json *txn_json;
txn_json = NULL;
HMAP_FOR_EACH (txn_table, struct ovsdb_txn_table, hmap_node,
&txn->txn_tables) {
struct json *txn_table_json = ovsdb_txn_table_to_json(txn_table);
if (!txn_json) {
txn_json = json_object_create();
}
json_object_put(txn_json, txn_table->table->schema->name,
txn_table_json);
}
return txn_json;
}
static struct ovsdb_error *
ovsdb_txn_row_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table,
const struct uuid *row_uuid, struct json *json)
{
const struct ovsdb_row *row = ovsdb_table_get_row(table, row_uuid);
if (json->type == JSON_NULL) {
if (!row) {
return ovsdb_syntax_error(NULL, NULL, "transaction deletes "
"row "UUID_FMT" that does not exist",
UUID_ARGS(row_uuid));
}
ovsdb_txn_row_delete(txn, row);
return NULL;
} else if (row) {
return ovsdb_row_from_json(ovsdb_txn_row_modify(txn, row),
json, NULL, NULL);
} else {
struct ovsdb_error *error;
struct ovsdb_row *new;
new = ovsdb_row_create(table);
*ovsdb_row_get_uuid_rw(new) = *row_uuid;
error = ovsdb_row_from_json(new, json, NULL, NULL);
if (error) {
ovsdb_row_destroy(new);
}
ovsdb_txn_row_insert(txn, new);
return error;
}
}
static struct ovsdb_error *
ovsdb_txn_table_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table,
struct json *json)
{
struct shash_node *node;
if (json->type != JSON_OBJECT) {
return ovsdb_syntax_error(json, NULL, "object expected");
}
SHASH_FOR_EACH (node, json->u.object) {
const char *uuid_string = node->name;
struct json *txn_row_json = node->data;
struct ovsdb_error *error;
struct uuid row_uuid;
if (!uuid_from_string(&row_uuid, uuid_string)) {
return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID",
uuid_string);
}
error = ovsdb_txn_row_from_json(txn, table, &row_uuid, txn_row_json);
if (error) {
return error;
}
}
return NULL;
}
struct ovsdb_error *
ovsdb_txn_from_json(struct ovsdb *db, const struct json *json,
struct ovsdb_txn **txnp)
{
struct ovsdb_error *error;
struct shash_node *node;
struct ovsdb_txn *txn;
*txnp = NULL;
if (json->type != JSON_OBJECT) {
return ovsdb_syntax_error(json, NULL, "object expected");
}
txn = ovsdb_txn_create(db);
SHASH_FOR_EACH (node, json->u.object) {
const char *table_name = node->name;
struct json *txn_table_json = node->data;
struct ovsdb_table *table;
table = shash_find_data(&db->tables, table_name);
if (!table) {
error = ovsdb_syntax_error(json, "unknown table",
"No table named %s.", table_name);
goto error;
}
error = ovsdb_txn_table_from_json(txn, table, txn_table_json);
if (error) {
goto error;
}
}
*txnp = txn;
return NULL;
error:
ovsdb_txn_abort(txn);
return error;
}
static struct ovsdb_txn_table *
ovsdb_txn_get_txn_table__(struct ovsdb_txn *txn,
const struct ovsdb_table *table,
uint32_t hash)
{
struct ovsdb_txn_table *txn_table;
HMAP_FOR_EACH_IN_BUCKET (txn_table, struct ovsdb_txn_table, hmap_node,
hash, &txn->txn_tables) {
if (txn_table->table == table) {
return txn_table;
}
}
return NULL;
}
static struct ovsdb_txn_table *
ovsdb_txn_get_txn_table(struct ovsdb_txn *txn, const struct ovsdb_table *table)
{
return ovsdb_txn_get_txn_table__(txn, table, hash_pointer(table, 0));
}
static struct ovsdb_txn_table *
ovsdb_txn_create_txn_table(struct ovsdb_txn *txn,
struct ovsdb_table *table)
{
uint32_t hash = hash_pointer(table, 0);
struct ovsdb_txn_table *txn_table;
txn_table = ovsdb_txn_get_txn_table__(txn, table, hash);
if (!txn_table) {
txn_table = xmalloc(sizeof *txn_table);
txn_table->table = table;
hmap_init(&txn_table->txn_rows);
hmap_insert(&txn->txn_tables, &txn_table->hmap_node, hash);
}
return txn_table;
}
static struct ovsdb_txn_row *
ovsdb_txn_row_create(struct ovsdb_txn_table *txn_table,
const struct ovsdb_row *old, struct ovsdb_row *new)
{
uint32_t hash = ovsdb_row_hash(old ? old : new);
struct ovsdb_txn_row *txn_row;
txn_row = xmalloc(sizeof *txn_row);
txn_row->old = (struct ovsdb_row *) old;
txn_row->new = new;
hmap_insert(&txn_table->txn_rows, &txn_row->hmap_node, hash);
return txn_row;
}
struct ovsdb_row *
ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
{
struct ovsdb_row *ro_row = (struct ovsdb_row *) ro_row_;
if (ro_row->txn_row) {
assert(ro_row == ro_row->txn_row->new);
return ro_row;
} else {
struct ovsdb_table *table = ro_row->table;
struct ovsdb_txn_table *txn_table;
struct ovsdb_row *rw_row;
txn_table = ovsdb_txn_create_txn_table(txn, table);
rw_row = ovsdb_row_clone(ro_row);
uuid_generate(ovsdb_row_get_version_rw(rw_row));
rw_row->txn_row = ovsdb_txn_row_create(txn_table, ro_row, rw_row);
hmap_replace(&table->rows, &ro_row->hmap_node, &rw_row->hmap_node);
return rw_row;
}
}
void
ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct ovsdb_row *row)
{
uint32_t hash = ovsdb_row_hash(row);
struct ovsdb_table *table = row->table;
struct ovsdb_txn_table *txn_table;
uuid_generate(ovsdb_row_get_version_rw(row));
txn_table = ovsdb_txn_create_txn_table(txn, table);
row->txn_row = ovsdb_txn_row_create(txn_table, NULL, row);
hmap_insert(&table->rows, &row->hmap_node, hash);
}
/* 'row' must be assumed destroyed upon return; the caller must not reference
* it again. */
void
ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_)
{
struct ovsdb_row *row = (struct ovsdb_row *) row_;
struct ovsdb_table *table = row->table;
struct ovsdb_txn_row *txn_row = row->txn_row;
struct ovsdb_txn_table *txn_table;
hmap_remove(&table->rows, &row->hmap_node);
if (!txn_row) {
txn_table = ovsdb_txn_create_txn_table(txn, table);
row->txn_row = ovsdb_txn_row_create(txn_table, row, NULL);
} else {
assert(txn_row->new == row);
if (txn_row->old) {
txn_row->new = NULL;
} else {
txn_table = ovsdb_txn_get_txn_table(txn, table);
hmap_remove(&txn_table->txn_rows, &txn_row->hmap_node);
}
ovsdb_row_destroy(row);
}
}

41
ovsdb/transaction.h Normal file
View File

@@ -0,0 +1,41 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_TRANSACTION_H
#define OVSDB_TRANSACTION_H 1
#include <stdbool.h>
#include "compiler.h"
struct ovsdb;
struct ovsdb_table;
struct uuid;
struct ovsdb_txn *ovsdb_txn_create(struct ovsdb *);
void ovsdb_txn_abort(struct ovsdb_txn *);
void ovsdb_txn_commit(struct ovsdb_txn *);
struct json *ovsdb_txn_to_json(const struct ovsdb_txn *);
struct ovsdb_error *ovsdb_txn_from_json(struct ovsdb *, const struct json *,
struct ovsdb_txn **)
WARN_UNUSED_RESULT;
struct ovsdb_row *ovsdb_txn_row_modify(struct ovsdb_txn *,
const struct ovsdb_row *);
void ovsdb_txn_row_insert(struct ovsdb_txn *, struct ovsdb_row *);
void ovsdb_txn_row_delete(struct ovsdb_txn *, const struct ovsdb_row *);
#endif /* ovsdb/transaction.h */

129
ovsdb/trigger.c Normal file
View File

@@ -0,0 +1,129 @@
/* Copyright (c) 2009 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 "trigger.h"
#include <assert.h>
#include <limits.h>
#include "json.h"
#include "jsonrpc.h"
#include "ovsdb.h"
#include "poll-loop.h"
static bool ovsdb_trigger_try(struct ovsdb *db, struct ovsdb_trigger *,
long long int now);
static void ovsdb_trigger_complete(struct ovsdb_trigger *);
void
ovsdb_trigger_init(struct ovsdb *db, struct ovsdb_trigger *trigger,
struct json *request, struct list *completion,
long long int now)
{
list_push_back(&db->triggers, &trigger->node);
trigger->completion = completion;
trigger->request = request;
trigger->result = NULL;
trigger->created = now;
trigger->timeout_msec = LLONG_MAX;
ovsdb_trigger_try(db, trigger, now);
}
void
ovsdb_trigger_destroy(struct ovsdb_trigger *trigger)
{
list_remove(&trigger->node);
json_destroy(trigger->request);
json_destroy(trigger->result);
}
bool
ovsdb_trigger_is_complete(const struct ovsdb_trigger *trigger)
{
return trigger->result != NULL;
}
struct json *
ovsdb_trigger_steal_result(struct ovsdb_trigger *trigger)
{
struct json *result = trigger->result;
trigger->result = NULL;
return result;
}
void
ovsdb_trigger_run(struct ovsdb *db, long long int now)
{
struct ovsdb_trigger *t, *next;
bool run_triggers;
run_triggers = db->run_triggers;
db->run_triggers = false;
LIST_FOR_EACH_SAFE (t, next, struct ovsdb_trigger, node, &db->triggers) {
if (run_triggers || now - t->created >= t->timeout_msec) {
ovsdb_trigger_try(db, t, now);
}
}
}
void
ovsdb_trigger_wait(struct ovsdb *db, long long int now)
{
if (db->run_triggers) {
poll_immediate_wake();
} else {
long long int deadline = LLONG_MAX;
struct ovsdb_trigger *t;
LIST_FOR_EACH (t, struct ovsdb_trigger, node, &db->triggers) {
if (t->created < LLONG_MAX - t->timeout_msec) {
long long int t_deadline = t->created + t->timeout_msec;
if (deadline > t_deadline) {
deadline = t_deadline;
if (now >= deadline) {
break;
}
}
}
}
if (deadline < LLONG_MAX) {
poll_timer_wait(MIN(deadline - now, INT_MAX));
}
}
}
static bool
ovsdb_trigger_try(struct ovsdb *db, struct ovsdb_trigger *t, long long int now)
{
t->result = ovsdb_execute(db, t->request, now - t->created,
&t->timeout_msec);
if (t->result) {
ovsdb_trigger_complete(t);
return true;
} else {
return false;
}
}
static void
ovsdb_trigger_complete(struct ovsdb_trigger *t)
{
assert(t->result != NULL);
list_remove(&t->node);
list_push_back(t->completion, &t->node);
}

44
ovsdb/trigger.h Normal file
View File

@@ -0,0 +1,44 @@
/* Copyright (c) 2009 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.
*/
#ifndef OVSDB_TRIGGER_H
#define OVSDB_TRIGGER_H 1
#include "list.h"
struct ovsdb;
struct ovsdb_trigger {
struct list node; /* !result: in struct ovsdb "triggers" list;
* result: in completion list. */
struct list *completion; /* Completion list. */
struct json *request; /* Database request. */
struct json *result; /* Result (null if none yet). */
long long int created; /* Time created. */
long long int timeout_msec; /* Max wait duration. */
};
void ovsdb_trigger_init(struct ovsdb *, struct ovsdb_trigger *,
struct json *request, struct list *completion,
long long int now);
void ovsdb_trigger_destroy(struct ovsdb_trigger *);
bool ovsdb_trigger_is_complete(const struct ovsdb_trigger *);
struct json *ovsdb_trigger_steal_result(struct ovsdb_trigger *);
void ovsdb_trigger_run(struct ovsdb *, long long int now);
void ovsdb_trigger_wait(struct ovsdb *, long long int now);
#endif /* ovsdb/trigger.h */

View File

@@ -15,6 +15,18 @@ TESTSUITE_AT = \
tests/jsonrpc.at \ tests/jsonrpc.at \
tests/timeval.at \ tests/timeval.at \
tests/lockfile.at \ tests/lockfile.at \
tests/ovsdb.at \
tests/ovsdb-file.at \
tests/ovsdb-types.at \
tests/ovsdb-data.at \
tests/ovsdb-column.at \
tests/ovsdb-table.at \
tests/ovsdb-row.at \
tests/ovsdb-condition.at \
tests/ovsdb-query.at \
tests/ovsdb-transaction.at \
tests/ovsdb-execution.at \
tests/ovsdb-trigger.at \
tests/stp.at \ tests/stp.at \
tests/ovs-vsctl.at \ tests/ovs-vsctl.at \
tests/lcov-post.at tests/lcov-post.at
@@ -89,6 +101,11 @@ noinst_PROGRAMS += tests/test-lockfile
tests_test_lockfile_SOURCES = tests/test-lockfile.c tests_test_lockfile_SOURCES = tests/test-lockfile.c
tests_test_lockfile_LDADD = lib/libopenvswitch.a tests_test_lockfile_LDADD = lib/libopenvswitch.a
noinst_PROGRAMS += tests/test-ovsdb
tests_test_ovsdb_SOURCES = tests/test-ovsdb.c
tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
EXTRA_DIST += tests/uuidfilt.pl
noinst_PROGRAMS += tests/test-sha1 noinst_PROGRAMS += tests/test-sha1
tests_test_sha1_SOURCES = tests/test-sha1.c tests_test_sha1_SOURCES = tests/test-sha1.c
tests_test_sha1_LDADD = lib/libopenvswitch.a tests_test_sha1_LDADD = lib/libopenvswitch.a

View File

@@ -11,6 +11,7 @@ OVS_CHECK_LCOV([test-csum], [0], [ignore])
AT_CLEANUP AT_CLEANUP
AT_SETUP([test flow classifier]) AT_SETUP([test flow classifier])
AT_KEYWORDS([slow])
OVS_CHECK_LCOV([test-classifier], [0], [ignore]) OVS_CHECK_LCOV([test-classifier], [0], [ignore])
AT_CLEANUP AT_CLEANUP

18
tests/ovsdb-column.at Normal file
View File

@@ -0,0 +1,18 @@
AT_BANNER([OVSDB -- columns])
OVSDB_CHECK_POSITIVE([ordinary column],
[[parse-column mycol '{"type": "integer"}']],
[[{"type":"integer"}]])
OVSDB_CHECK_POSITIVE([immutable column],
[[parse-column mycol '{"type": "real", "mutable": false}']],
[[{"mutable":false,"type":"real"}]])
OVSDB_CHECK_POSITIVE([ephemeral column],
[[parse-column mycol '{"type": "uuid", "ephemeral": true}']],
[[{"ephemeral":true,"type":"uuid"}]])
OVSDB_CHECK_POSITIVE([column with comment],
[[parse-column mycol '{"type": "boolean",
"comment": "extra information about this column"}']],
[[{"comment":"extra information about this column","type":"boolean"}]])

558
tests/ovsdb-condition.at Normal file
View File

@@ -0,0 +1,558 @@
AT_BANNER([OVSDB -- conditions])
OVSDB_CHECK_POSITIVE([null condition],
[[parse-conditions \
'{"columns": {"name": {"type": "string"}}}' \
'[]']],
[[[]]])
OVSDB_CHECK_POSITIVE([conditions on scalars],
[[parse-conditions \
'{"columns":
{"i": {"type": "integer"},
"r": {"type": "real"},
"b": {"type": "boolean"},
"s": {"type": "string"},
"u": {"type": "uuid"}}}' \
'[["i", "==", 0]]' \
'[["i", "!=", 1]]' \
'[["i", "<", 2]]' \
'[["i", "<=", 3]]' \
'[["i", ">", 4]]' \
'[["i", ">=", 5]]' \
'[["i", "includes", 6]]' \
'[["i", "excludes", 7]]' \
'[["r", "==", 0.5]]' \
'[["r", "!=", 1.5]]' \
'[["r", "<", 2.5]]' \
'[["r", "<=", 3.5]]' \
'[["r", ">", 4.5]]' \
'[["r", ">=", 5.5]]' \
'[["r", "includes", 6.5]]' \
'[["r", "excludes", 7.5]]' \
'[["b", "==", true]]' \
'[["b", "!=", false]]' \
'[["b", "includes", false]]' \
'[["b", "excludes", true]]' \
'[["s", "==", "a"]]' \
'[["s", "!=", "b"]]' \
'[["s", "includes", "c"]]' \
'[["s", "excludes", "d"]]' \
'[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \
'[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
'[["u", "includes", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \
'[["u", "excludes", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]']],
[[[["i","==",0]]
[["i","!=",1]]
[["i","<",2]]
[["i","<=",3]]
[["i",">",4]]
[["i",">=",5]]
[["i","includes",6]]
[["i","excludes",7]]
[["r","==",0.5]]
[["r","!=",1.5]]
[["r","<",2.5]]
[["r","<=",3.5]]
[["r",">",4.5]]
[["r",">=",5.5]]
[["r","includes",6.5]]
[["r","excludes",7.5]]
[["b","==",true]]
[["b","!=",false]]
[["b","includes",false]]
[["b","excludes",true]]
[["s","==","a"]]
[["s","!=","b"]]
[["s","includes","c"]]
[["s","excludes","d"]]
[["u","==",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]
[["u","!=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]]
[["u","includes",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]
[["u","excludes",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]]]],
[condition])
AT_SETUP([disallowed conditions on scalars])
AT_KEYWORDS([ovsdb negative condition])
OVS_CHECK_LCOV([[test-ovsdb parse-conditions \
'{"columns":
{"i": {"type": "integer"},
"r": {"type": "real"},
"b": {"type": "boolean"},
"s": {"type": "string"},
"u": {"type": "uuid"}}}' \
'[["b", ">", true]]' \
'[["b", ">=", false]]' \
'[["b", "<", false]]' \
'[["b", "<=", false]]' \
'[["s", ">", "a"]]' \
'[["s", ">=", "b"]]' \
'[["s", "<", "c"]]' \
'[["s", "<=", "d"]]' \
'[["u", ">", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]' \
'[["u", ">=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
'[["u", "<", ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]' \
'[["u", "<=", ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]']],
[1], [],
[[test-ovsdb: syntax "["b",">",true]": syntax error: Type mismatch: ">" operator may not be applied to column b of type boolean.
test-ovsdb: syntax "["b",">=",false]": syntax error: Type mismatch: ">=" operator may not be applied to column b of type boolean.
test-ovsdb: syntax "["b","<",false]": syntax error: Type mismatch: "<" operator may not be applied to column b of type boolean.
test-ovsdb: syntax "["b","<=",false]": syntax error: Type mismatch: "<=" operator may not be applied to column b of type boolean.
test-ovsdb: syntax "["s",">","a"]": syntax error: Type mismatch: ">" operator may not be applied to column s of type string.
test-ovsdb: syntax "["s",">=","b"]": syntax error: Type mismatch: ">=" operator may not be applied to column s of type string.
test-ovsdb: syntax "["s","<","c"]": syntax error: Type mismatch: "<" operator may not be applied to column s of type string.
test-ovsdb: syntax "["s","<=","d"]": syntax error: Type mismatch: "<=" operator may not be applied to column s of type string.
test-ovsdb: syntax "["u",">",["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]": syntax error: Type mismatch: ">" operator may not be applied to column u of type uuid.
test-ovsdb: syntax "["u",">=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: ">=" operator may not be applied to column u of type uuid.
test-ovsdb: syntax "["u","<",["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]": syntax error: Type mismatch: "<" operator may not be applied to column u of type uuid.
test-ovsdb: syntax "["u","<=",["uuid","62315898-64e0-40b9-b26f-ff74225303e6"]]": syntax error: Type mismatch: "<=" operator may not be applied to column u of type uuid.
]])
AT_CLEANUP
OVSDB_CHECK_POSITIVE([conditions on sets],
[[parse-conditions \
'{"columns":
{"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
"r": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
"b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
"s": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
"u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
'[["i", "==", ["set", []]]]' \
'[["i", "!=", ["set", [1]]]]' \
'[["i", "includes", ["set", [1, 2]]]]' \
'[["i", "excludes", ["set", [1, 2, 3]]]]' \
'[["r", "==", ["set", []]]]' \
'[["r", "!=", ["set", [1.5]]]]' \
'[["r", "includes", ["set", [1.5, 2.5]]]]' \
'[["r", "excludes", ["set", [1.5, 2.5, 3.5]]]]' \
'[["b", "==", ["set", [true]]]]' \
'[["b", "!=", ["set", [false]]]]' \
'[["b", "includes", ["set", [false]]]]' \
'[["b", "excludes", ["set", [true, false]]]]' \
'[["s", "==", ["set", ["a"]]]]' \
'[["s", "!=", ["set", ["a", "b"]]]]' \
'[["s", "includes", ["set", ["c"]]]]' \
'[["s", "excludes", ["set", ["c", "d"]]]]' \
'[["u", "==",
["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]' \
'[["u", "==",
["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]]]' \
'[["u", "includes",
["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"],
["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]]' \
'[["u", "excludes",
["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"],
["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],
["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]]]]]' \
]],
[[[["i","==",["set",[]]]]
[["i","!=",["set",[1]]]]
[["i","includes",["set",[1,2]]]]
[["i","excludes",["set",[1,2,3]]]]
[["r","==",["set",[]]]]
[["r","!=",["set",[1.5]]]]
[["r","includes",["set",[1.5,2.5]]]]
[["r","excludes",["set",[1.5,2.5,3.5]]]]
[["b","==",["set",[true]]]]
[["b","!=",["set",[false]]]]
[["b","includes",["set",[false]]]]
[["b","excludes",["set",[false,true]]]]
[["s","==",["set",["a"]]]]
[["s","!=",["set",["a","b"]]]]
[["s","includes",["set",["c"]]]]
[["s","excludes",["set",["c","d"]]]]
[["u","==",["set",[["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
[["u","==",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
[["u","includes",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
[["u","excludes",["set",[["uuid","62315898-64e0-40b9-b26f-ff74225303e6"],["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]]],
[condition])
OVSDB_CHECK_POSITIVE([condition sorting],
[[parse-conditions \
'{"columns": {"i": {"type": "integer"}}}' \
'[["i", "excludes", 7],
["i", "!=", 8],
["i", "==", 1],
["i", "includes", 2],
["i", "<=", 3],
["i", "<", 4],
["i", ">", 6],
["i", ">=", 5],
["_uuid", "==", ["uuid", "d50e85c6-8ae7-4b16-b69e-4395928bd9be"]]]']],
[[[["_uuid","==",["uuid","d50e85c6-8ae7-4b16-b69e-4395928bd9be"]],["i","==",1],["i","includes",2],["i","<=",3],["i","<",4],["i",">=",5],["i",">",6],["i","excludes",7],["i","!=",8]]]])
OVSDB_CHECK_POSITIVE([evaluating null condition],
[[evaluate-conditions \
'{"columns": {"i": {"type": "integer"}}}' \
'[[]]' \
'[{"i": 0},
{"i": 1},
{"i": 2}']]],
[condition 0: TTT])
OVSDB_CHECK_POSITIVE([evaluating conditions on integers],
[[evaluate-conditions \
'{"columns": {"i": {"type": "integer"}}}' \
'[[["i", "<", 1]],
[["i", "<=", 1]],
[["i", "==", 1]],
[["i", "!=", 1]],
[["i", ">=", 1]],
[["i", ">", 1]],
[["i", "includes", 1]],
[["i", "excludes", 1]]]' \
'[{"i": 0},
{"i": 1},
{"i": 2}']]],
[condition 0: T--
condition 1: TT-
condition 2: -T-
condition 3: T-T
condition 4: -TT
condition 5: --T
condition 6: -T-
condition 7: T-T], [condition])
OVSDB_CHECK_POSITIVE([evaluating conditions on reals],
[[evaluate-conditions \
'{"columns": {"r": {"type": "real"}}}' \
'[[["r", "<", 5.0]],
[["r", "<=", 5.0]],
[["r", "==", 5.0]],
[["r", "!=", 5.0]],
[["r", ">=", 5.0]],
[["r", ">", 5.0]],
[["r", "includes", 5.0]],
[["r", "excludes", 5.0]]]' \
'[{"r": 0},
{"r": 5.0},
{"r": 5.1}']]],
[condition 0: T--
condition 1: TT-
condition 2: -T-
condition 3: T-T
condition 4: -TT
condition 5: --T
condition 6: -T-
condition 7: T-T], [condition])
OVSDB_CHECK_POSITIVE([evaluating conditions on booleans],
[[evaluate-conditions \
'{"columns": {"b": {"type": "boolean"}}}' \
'[[["b", "==", true]],
[["b", "!=", true]],
[["b", "includes", true]],
[["b", "excludes", true]],
[["b", "==", false]],
[["b", "!=", false]],
[["b", "includes", false]],
[["b", "excludes", false]]]' \
'[{"b": true},
{"b": false}']]],
[condition 0: T-
condition 1: -T
condition 2: T-
condition 3: -T
condition 4: -T
condition 5: T-
condition 6: -T
condition 7: T-], [condition])
OVSDB_CHECK_POSITIVE([evaluating conditions on strings],
[[evaluate-conditions \
'{"columns": {"s": {"type": "string"}}}' \
'[[["s", "==", ""]],
[["s", "!=", ""]],
[["s", "includes", ""]],
[["s", "excludes", ""]],
[["s", "==", "foo"]],
[["s", "!=", "foo"]],
[["s", "includes", "foo"]],
[["s", "excludes", "foo"]]]' \
'[{"s": ""},
{"s": "foo"},
{"s": "xxx"}']]],
[condition 0: T--
condition 1: -TT
condition 2: T--
condition 3: -TT
condition 4: -T-
condition 5: T-T
condition 6: -T-
condition 7: T-T], [condition])
OVSDB_CHECK_POSITIVE([evaluating conditions on UUIDs],
[[evaluate-conditions \
'{"columns": {"u": {"type": "uuid"}}}' \
'[[["u", "==", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
[["u", "!=", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
[["u", "includes", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
[["u", "excludes", ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]]],
[["u", "==", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]],
[["u", "!=", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]],
[["u", "includes", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]],
[["u", "excludes", ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]]]]' \
'[{"u": ["uuid", "8a1dbdb8-416f-4ce9-affa-3332691714b6"]},
{"u": ["uuid", "06151f9d-62d6-4f59-8504-e9765107faa9"]},
{"u": ["uuid", "00000000-0000-0000-0000-000000000000"]}']]],
[condition 0: T--
condition 1: -TT
condition 2: T--
condition 3: -TT
condition 4: -T-
condition 5: T-T
condition 6: -T-
condition 7: T-T], [condition])
OVSDB_CHECK_POSITIVE([evaluating conditions on sets],
[[evaluate-conditions \
'{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \
'[[["i", "==", ["set", []]]],
[["i", "==", ["set", [0]]]],
[["i", "==", ["set", [1]]]],
[["i", "==", ["set", [0, 1]]]],
[["i", "==", ["set", [2]]]],
[["i", "==", ["set", [2, 0]]]],
[["i", "==", ["set", [2, 1]]]],
[["i", "==", ["set", [2, 1, 0]]]],
[["i", "!=", ["set", []]]],
[["i", "!=", ["set", [0]]]],
[["i", "!=", ["set", [1]]]],
[["i", "!=", ["set", [0, 1]]]],
[["i", "!=", ["set", [2]]]],
[["i", "!=", ["set", [2, 0]]]],
[["i", "!=", ["set", [2, 1]]]],
[["i", "!=", ["set", [2, 1, 0]]]],
[["i", "includes", ["set", []]]],
[["i", "includes", ["set", [0]]]],
[["i", "includes", ["set", [1]]]],
[["i", "includes", ["set", [0, 1]]]],
[["i", "includes", ["set", [2]]]],
[["i", "includes", ["set", [2, 0]]]],
[["i", "includes", ["set", [2, 1]]]],
[["i", "includes", ["set", [2, 1, 0]]]],
[["i", "excludes", ["set", []]]],
[["i", "excludes", ["set", [0]]]],
[["i", "excludes", ["set", [1]]]],
[["i", "excludes", ["set", [0, 1]]]],
[["i", "excludes", ["set", [2]]]],
[["i", "excludes", ["set", [2, 0]]]],
[["i", "excludes", ["set", [2, 1]]]],
[["i", "excludes", ["set", [2, 1, 0]]]]]' \
'[{"i": ["set", []]},
{"i": ["set", [0]]},
{"i": ["set", [1]]},
{"i": ["set", [0, 1]]},
{"i": ["set", [2]]},
{"i": ["set", [2, 0]]},
{"i": ["set", [2, 1]]},
{"i": ["set", [2, 1, 0]]}]']],
[dnl
condition 0: T---- ---
condition 1: -T--- ---
condition 2: --T-- ---
condition 3: ---T- ---
condition 4: ----T ---
condition 5: ----- T--
condition 6: ----- -T-
condition 7: ----- --T
condition 8: -TTTT TTT
condition 9: T-TTT TTT
condition 10: TT-TT TTT
condition 11: TTT-T TTT
condition 12: TTTT- TTT
condition 13: TTTTT -TT
condition 14: TTTTT T-T
condition 15: TTTTT TT-
condition 16: TTTTT TTT
condition 17: -T-T- T-T
condition 18: --TT- -TT
condition 19: ---T- --T
condition 20: ----T TTT
condition 21: ----- T-T
condition 22: ----- -TT
condition 23: ----- --T
condition 24: TTTTT TTT
condition 25: T-T-T -T-
condition 26: TT--T T--
condition 27: T---T ---
condition 28: TTTT- ---
condition 29: T-T-- ---
condition 30: TT--- ---
condition 31: T---- ---], [condition])
# This is the same as the "set" test except that it adds values,
# all of which always match.
OVSDB_CHECK_POSITIVE([evaluating conditions on maps (1)],
[[evaluate-conditions \
'{"columns": {"i": {"type": {"key": "integer",
"value": "boolean",
"min": 0,
"max": "unlimited"}}}}' \
'[[["i", "==", ["map", []]]],
[["i", "==", ["map", [[0, true]]]]],
[["i", "==", ["map", [[1, false]]]]],
[["i", "==", ["map", [[0, true], [1, false]]]]],
[["i", "==", ["map", [[2, true]]]]],
[["i", "==", ["map", [[2, true], [0, true]]]]],
[["i", "==", ["map", [[2, true], [1, false]]]]],
[["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "!=", ["map", []]]],
[["i", "!=", ["map", [[0, true]]]]],
[["i", "!=", ["map", [[1, false]]]]],
[["i", "!=", ["map", [[0, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true]]]]],
[["i", "!=", ["map", [[2, true], [0, true]]]]],
[["i", "!=", ["map", [[2, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "includes", ["map", []]]],
[["i", "includes", ["map", [[0, true]]]]],
[["i", "includes", ["map", [[1, false]]]]],
[["i", "includes", ["map", [[0, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true]]]]],
[["i", "includes", ["map", [[2, true], [0, true]]]]],
[["i", "includes", ["map", [[2, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "excludes", ["map", []]]],
[["i", "excludes", ["map", [[0, true]]]]],
[["i", "excludes", ["map", [[1, false]]]]],
[["i", "excludes", ["map", [[0, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true]]]]],
[["i", "excludes", ["map", [[2, true], [0, true]]]]],
[["i", "excludes", ["map", [[2, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]' \
'[{"i": ["map", []]},
{"i": ["map", [[0, true]]]},
{"i": ["map", [[1, false]]]},
{"i": ["map", [[0, true], [1, false]]]},
{"i": ["map", [[2, true]]]},
{"i": ["map", [[2, true], [0, true]]]},
{"i": ["map", [[2, true], [1, false]]]},
{"i": ["map", [[2, true], [1, false], [0, true]]]}]']],
[dnl
condition 0: T---- ---
condition 1: -T--- ---
condition 2: --T-- ---
condition 3: ---T- ---
condition 4: ----T ---
condition 5: ----- T--
condition 6: ----- -T-
condition 7: ----- --T
condition 8: -TTTT TTT
condition 9: T-TTT TTT
condition 10: TT-TT TTT
condition 11: TTT-T TTT
condition 12: TTTT- TTT
condition 13: TTTTT -TT
condition 14: TTTTT T-T
condition 15: TTTTT TT-
condition 16: TTTTT TTT
condition 17: -T-T- T-T
condition 18: --TT- -TT
condition 19: ---T- --T
condition 20: ----T TTT
condition 21: ----- T-T
condition 22: ----- -TT
condition 23: ----- --T
condition 24: TTTTT TTT
condition 25: T-T-T -T-
condition 26: TT--T T--
condition 27: T---T ---
condition 28: TTTT- ---
condition 29: T-T-- ---
condition 30: TT--- ---
condition 31: T---- ---], [condition])
# This is the same as the "set" test except that it adds values,
# and those values don't always match.
OVSDB_CHECK_POSITIVE([evaluating conditions on maps (2)],
[[evaluate-conditions \
'{"columns": {"i": {"type": {"key": "integer",
"value": "boolean",
"min": 0,
"max": "unlimited"}}}}' \
'[[["i", "==", ["map", []]]],
[["i", "==", ["map", [[0, true]]]]],
[["i", "==", ["map", [[1, false]]]]],
[["i", "==", ["map", [[0, true], [1, false]]]]],
[["i", "==", ["map", [[2, true]]]]],
[["i", "==", ["map", [[2, true], [0, true]]]]],
[["i", "==", ["map", [[2, true], [1, false]]]]],
[["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "!=", ["map", []]]],
[["i", "!=", ["map", [[0, true]]]]],
[["i", "!=", ["map", [[1, false]]]]],
[["i", "!=", ["map", [[0, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true]]]]],
[["i", "!=", ["map", [[2, true], [0, true]]]]],
[["i", "!=", ["map", [[2, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "includes", ["map", []]]],
[["i", "includes", ["map", [[0, true]]]]],
[["i", "includes", ["map", [[1, false]]]]],
[["i", "includes", ["map", [[0, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true]]]]],
[["i", "includes", ["map", [[2, true], [0, true]]]]],
[["i", "includes", ["map", [[2, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "excludes", ["map", []]]],
[["i", "excludes", ["map", [[0, true]]]]],
[["i", "excludes", ["map", [[1, false]]]]],
[["i", "excludes", ["map", [[0, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true]]]]],
[["i", "excludes", ["map", [[2, true], [0, true]]]]],
[["i", "excludes", ["map", [[2, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]' \
'[{"i": ["map", []]},
{"i": ["map", [[0, true]]]},
{"i": ["map", [[0, false]]]},
{"i": ["map", [[1, false]]]},
{"i": ["map", [[1, true]]]},
{"i": ["map", [[0, true], [1, false]]]},
{"i": ["map", [[0, true], [1, true]]]},
{"i": ["map", [[2, true]]]},
{"i": ["map", [[2, false]]]},
{"i": ["map", [[2, true], [0, true]]]},
{"i": ["map", [[2, false], [0, true]]]},
{"i": ["map", [[2, true], [1, false]]]},
{"i": ["map", [[2, true], [1, true]]]},
{"i": ["map", [[2, true], [1, false], [0, true]]]},
{"i": ["map", [[2, true], [1, false], [0, false]]]}]']],
[dnl
condition 0: T---- ----- -----
condition 1: -T--- ----- -----
condition 2: ---T- ----- -----
condition 3: ----- T---- -----
condition 4: ----- --T-- -----
condition 5: ----- ----T -----
condition 6: ----- ----- -T---
condition 7: ----- ----- ---T-
condition 8: -TTTT TTTTT TTTTT
condition 9: T-TTT TTTTT TTTTT
condition 10: TTT-T TTTTT TTTTT
condition 11: TTTTT -TTTT TTTTT
condition 12: TTTTT TT-TT TTTTT
condition 13: TTTTT TTTT- TTTTT
condition 14: TTTTT TTTTT T-TTT
condition 15: TTTTT TTTTT TTT-T
condition 16: TTTTT TTTTT TTTTT
condition 17: -T--- TT--T T--T-
condition 18: ---T- T---- -T-TT
condition 19: ----- T---- ---T-
condition 20: ----- --T-T -TTTT
condition 21: ----- ----T ---T-
condition 22: ----- ----- -T-TT
condition 23: ----- ----- ---T-
condition 24: TTTTT TTTTT TTTTT
condition 25: T-TTT --TT- -TT-T
condition 26: TTT-T -TTTT T-T--
condition 27: T-T-T --TT- --T--
condition 28: TTTTT TT-T- T----
condition 29: T-TTT ---T- -----
condition 30: TTT-T -T-T- T----
condition 31: T-T-T ---T- -----], [condition])

259
tests/ovsdb-data.at Normal file
View File

@@ -0,0 +1,259 @@
AT_BANNER([OVSDB -- atoms])
OVSDB_CHECK_POSITIVE([integer atom],
[[parse-atoms '["integer"]' \
'[0]' \
'[-1]' \
'[1e3]' \
'[9223372036854775807]' \
'[-9223372036854775808]' ]],
[0
-1
1000
9223372036854775807
-9223372036854775808])
OVSDB_CHECK_POSITIVE([real atom],
[[parse-atoms '["real"]' \
'[0]' \
'[0.0]' \
'[-0.0]' \
'[-1.25]' \
'[1e3]' \
'[1e37]' \
'[0.00390625]' ]],
[0
0
0
-1.25
1000
1e+37
0.00390625])
OVSDB_CHECK_POSITIVE([boolean atom],
[[parse-atoms '["boolean"]' '[true]' '[false]' ]],
[true
false])
OVSDB_CHECK_POSITIVE([string atom],
[[parse-atoms '["string"]' '[""]' '["true"]' '["\"\\\/\b\f\n\r\t"]']],
[""
"true"
"\"\\/\b\f\n\r\t"])
OVSDB_CHECK_POSITIVE([uuid atom],
[[parse-atoms '["uuid"]' '["uuid", "550e8400-e29b-41d4-a716-446655440000"]']],
[[["uuid","550e8400-e29b-41d4-a716-446655440000"]]])
OVSDB_CHECK_POSITIVE([integer atom sorting],
[[sort-atoms '["integer"]' '[55,0,-1,2,1]']],
[[[-1,0,1,2,55]]])
OVSDB_CHECK_POSITIVE([real atom sorting],
[[sort-atoms '["real"]' '[1.25,1.23,0.0,-0.0,-1e99]']],
[[[-1e+99,0,0,1.23,1.25]]])
OVSDB_CHECK_POSITIVE([boolean atom sorting],
[[sort-atoms '["boolean"]' '[true,false,true,false,false]']],
[[[false,false,false,true,true]]])
OVSDB_CHECK_POSITIVE([string atom sorting],
[[sort-atoms '["string"]' '["abd","abc","\b","xxx"]']],
[[["\b","abc","abd","xxx"]]])
OVSDB_CHECK_POSITIVE([uuid atom sorting],
[[sort-atoms '["uuid"]' '[
["uuid", "00000000-0000-0000-0000-000000000001"],
["uuid", "00000000-1000-0000-0000-000000000000"],
["uuid", "00000000-0000-1000-0000-000000000000"],
["uuid", "00010000-0000-0000-0000-000000000000"],
["uuid", "00000000-0000-0000-0000-000000000100"],
["uuid", "00000000-0000-0000-0000-000100000000"],
["uuid", "00000000-0000-0010-0000-000000000000"],
["uuid", "00000100-0000-0000-0000-000000000000"],
["uuid", "00000000-0000-0001-0000-000000000000"],
["uuid", "00000000-0000-0000-0000-000001000000"],
["uuid", "01000000-0000-0000-0000-000000000000"],
["uuid", "00000000-0000-0000-0000-000000001000"],
["uuid", "00000000-0000-0000-0000-000010000000"],
["uuid", "00000000-0000-0000-0000-010000000000"],
["uuid", "00000000-0000-0100-0000-000000000000"],
["uuid", "10000000-0000-0000-0000-000000000000"],
["uuid", "00000000-0000-0000-0000-000000000010"],
["uuid", "00000000-0100-0000-0000-000000000000"],
["uuid", "00000000-0000-0000-0100-000000000000"],
["uuid", "00000000-0000-0000-0001-000000000000"],
["uuid", "00000010-0000-0000-0000-000000000000"],
["uuid", "00000000-0000-0000-0010-000000000000"],
["uuid", "00000000-0000-0000-0000-000000010000"],
["uuid", "00000000-0000-0000-1000-000000000000"],
["uuid", "00000000-0000-0000-0000-100000000000"],
["uuid", "00000000-0000-0000-0000-001000000000"],
["uuid", "00000000-0000-0000-0000-000000100000"],
["uuid", "00000000-0000-0000-0000-000000000000"],
["uuid", "00000000-0010-0000-0000-000000000000"],
["uuid", "00100000-0000-0000-0000-000000000000"],
["uuid", "00000000-0001-0000-0000-000000000000"],
["uuid", "00000001-0000-0000-0000-000000000000"],
["uuid", "00001000-0000-0000-0000-000000000000"]]']],
[[[["uuid","00000000-0000-0000-0000-000000000000"],["uuid","00000000-0000-0000-0000-000000000001"],["uuid","00000000-0000-0000-0000-000000000010"],["uuid","00000000-0000-0000-0000-000000000100"],["uuid","00000000-0000-0000-0000-000000001000"],["uuid","00000000-0000-0000-0000-000000010000"],["uuid","00000000-0000-0000-0000-000000100000"],["uuid","00000000-0000-0000-0000-000001000000"],["uuid","00000000-0000-0000-0000-000010000000"],["uuid","00000000-0000-0000-0000-000100000000"],["uuid","00000000-0000-0000-0000-001000000000"],["uuid","00000000-0000-0000-0000-010000000000"],["uuid","00000000-0000-0000-0000-100000000000"],["uuid","00000000-0000-0000-0001-000000000000"],["uuid","00000000-0000-0000-0010-000000000000"],["uuid","00000000-0000-0000-0100-000000000000"],["uuid","00000000-0000-0000-1000-000000000000"],["uuid","00000000-0000-0001-0000-000000000000"],["uuid","00000000-0000-0010-0000-000000000000"],["uuid","00000000-0000-0100-0000-000000000000"],["uuid","00000000-0000-1000-0000-000000000000"],["uuid","00000000-0001-0000-0000-000000000000"],["uuid","00000000-0010-0000-0000-000000000000"],["uuid","00000000-0100-0000-0000-000000000000"],["uuid","00000000-1000-0000-0000-000000000000"],["uuid","00000001-0000-0000-0000-000000000000"],["uuid","00000010-0000-0000-0000-000000000000"],["uuid","00000100-0000-0000-0000-000000000000"],["uuid","00001000-0000-0000-0000-000000000000"],["uuid","00010000-0000-0000-0000-000000000000"],["uuid","00100000-0000-0000-0000-000000000000"],["uuid","01000000-0000-0000-0000-000000000000"],["uuid","10000000-0000-0000-0000-000000000000"]]]])
OVSDB_CHECK_NEGATIVE([real not acceptable integer atom],
[[parse-atoms '["integer"]' '[0.5]' ]],
[expected integer])
OVSDB_CHECK_NEGATIVE([string "true" not acceptable boolean atom],
[[parse-atoms '["boolean"]' '["true"]' ]],
[expected boolean])
OVSDB_CHECK_NEGATIVE([integer not acceptable string atom],
[[parse-atoms '["string"]' '[1]']],
[expected string])
OVSDB_CHECK_NEGATIVE([uuid atom must be expressed as array],
[[parse-atoms '["uuid"]' '["550e8400-e29b-41d4-a716-446655440000"]']],
[[expected ["uuid", <string>]]])
AT_BANNER([OSVDB -- simple data])
OVSDB_CHECK_POSITIVE([integer datum],
[[parse-data '["integer"]' '[0]' '[1]' '[-1]']],
[0
1
-1])
OVSDB_CHECK_POSITIVE([real datum],
[[parse-data '["real"]' '[0]' '[1.0]' '[-1.25]']],
[0
1
-1.25])
OVSDB_CHECK_POSITIVE([boolean datum],
[[parse-data '["boolean"]' '[true]' '[false]' ]],
[true
false])
OVSDB_CHECK_POSITIVE([string datum],
[[parse-data '["string"]' '[""]' '["true"]' '["\"\\\/\b\f\n\r\t"]']],
[""
"true"
"\"\\/\b\f\n\r\t"])
AT_BANNER([OVSDB -- set data])
OVSDB_CHECK_POSITIVE([optional boolean],
[[parse-data '{"key": "boolean", "min": 0}' \
'["set", [true]]' \
'["set", [false]]' \
'["set", []]']],
[[["set",[true]]
["set",[false]]
["set",[]]]],
[set])
OVSDB_CHECK_POSITIVE([set of 0 or more integers],
[[parse-data '{"key": "integer", "min": 0, "max": "unlimited"}' \
'["set", [0]]' \
'["set", [0, 1]]' \
'["set", [0, 1, 2]]' \
'["set", [0, 1, 2, 3, 4, 5]]' \
'["set", [0, 1, 2, 3, 4, 5, 6, 7, 8]]' \
'["set", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]']],
[[["set",[0]]
["set",[0,1]]
["set",[0,1,2]]
["set",[0,1,2,3,4,5]]
["set",[0,1,2,3,4,5,6,7,8]]
["set",[0,1,2,3,4,5,6,7,8,9,10]]]])
OVSDB_CHECK_POSITIVE([set of 1 to 3 uuids],
[[parse-data '{"key": "uuid", "min": 1, "max": 3}' \
'["set", [["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]' \
'["set", [["uuid", "c5051240-30ff-43ed-b4b9-93cf3f050813"],
["uuid", "90558331-09af-4d2f-a572-509cad2e9088"],
["uuid", "550e8400-e29b-41d4-a716-446655440000"]]]']],
[[["set",[["uuid","550e8400-e29b-41d4-a716-446655440000"]]]
["set",[["uuid","550e8400-e29b-41d4-a716-446655440000"],["uuid","90558331-09af-4d2f-a572-509cad2e9088"],["uuid","c5051240-30ff-43ed-b4b9-93cf3f050813"]]]]])
OVSDB_CHECK_POSITIVE([set of 0 to 3 strings],
[[parse-data '{"key": "string", "min": 0, "max": 3}' \
'["set", []]' \
'["set", ["a relatively long string"]]' \
'["set", ["short string", "a relatively long string"]]' \
'["set", ["zzz", "short string", "a relatively long string"]]']],
[[["set",[]]
["set",["a relatively long string"]]
["set",["a relatively long string","short string"]]
["set",["a relatively long string","short string","zzz"]]]])
OVSDB_CHECK_NEGATIVE([duplicate boolean not allowed in set],
[[parse-data '{"key": "boolean", "max": 5}' '["set", [true, true]]']],
[ovsdb error: set contains duplicate])
OVSDB_CHECK_NEGATIVE([duplicate integer not allowed in set],
[[parse-data '{"key": "integer", "max": 5}' '["set", [1, 2, 3, 1]]']],
[ovsdb error: set contains duplicate])
OVSDB_CHECK_NEGATIVE([duplicate real not allowed in set],
[[parse-data '{"key": "real", "max": 5}' '["set", [0.0, -0.0]]']],
[ovsdb error: set contains duplicate])
OVSDB_CHECK_NEGATIVE([duplicate string not allowed in set],
[[parse-data '{"key": "string", "max": 5}' '["set", ["asdf", "ASDF", "asdf"]]']],
[ovsdb error: set contains duplicate])
OVSDB_CHECK_NEGATIVE([duplicate uuid not allowed in set],
[[parse-data '{"key": "uuid", "max": 5}' \
'["set", [["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"],
["uuid", "355ad037-f1da-40aa-b47c-ff9c7e8c6a38"],
["uuid", "7ef21525-0088-4a28-a418-5518413e43ea"]]]']],
[ovsdb error: set contains duplicate])
AT_BANNER([OVSDB -- map data])
OVSDB_CHECK_POSITIVE([map of 1 integer to boolean],
[[parse-data '{"key": "integer", "value": "boolean"}' \
'["map", [[1, true]]]']],
[[["map",[[1,true]]]]])
OVSDB_CHECK_POSITIVE([map of at least 1 integer to boolean],
[[parse-data '{"key": "integer", "value": "boolean", "max": "unlimited"}' \
'["map", [[1, true]]]' \
'["map", [[0, true], [1, false], [2, true], [3, true], [4, true]]]' \
'["map", [[3, false], [0, true], [4, false]]]']],
[[["map",[[1,true]]]
["map",[[0,true],[1,false],[2,true],[3,true],[4,true]]]
["map",[[0,true],[3,false],[4,false]]]]])
OVSDB_CHECK_POSITIVE([map of 1 boolean to integer],
[[parse-data '{"key": "boolean", "value": "integer"}' \
'["map", [[true, 1]]]']],
[[["map",[[true,1]]]]])
OVSDB_CHECK_POSITIVE([map of 5 uuid to real],
[[parse-data '{"key": "uuid", "value": "real", "min": 5, "max": 5}' \
'["map", [[["uuid", "cad8542b-6ee1-486b-971b-7dcbf6e14979"], 1.0],
[["uuid", "6b94b968-2702-4f64-9457-314a34d69b8c"], 2.0],
[["uuid", "d2c4a168-24de-47eb-a8a3-c1abfc814979"], 3.0],
[["uuid", "25bfa475-d072-4f60-8be1-00f48643e9cb"], 4.0],
[["uuid", "1c92b8ca-d5e4-4628-a85d-1dc2d099a99a"], 5.0]]]']],
[[["map",[[["uuid","1c92b8ca-d5e4-4628-a85d-1dc2d099a99a"],5],[["uuid","25bfa475-d072-4f60-8be1-00f48643e9cb"],4],[["uuid","6b94b968-2702-4f64-9457-314a34d69b8c"],2],[["uuid","cad8542b-6ee1-486b-971b-7dcbf6e14979"],1],[["uuid","d2c4a168-24de-47eb-a8a3-c1abfc814979"],3]]]]])
OVSDB_CHECK_POSITIVE([map of 10 string to string],
[[parse-data '{"key": "string", "value": "string", "min": 10, "max": 10}' \
'["map", [["2 gills", "1 chopin"],
["2 chopins", "1 pint"],
["2 pints", "1 quart"],
["2 quarts", "1 pottle"],
["2 pottles", "1 gallon"],
["2 gallons", "1 peck"],
["2 pecks", "1 demibushel"],
["2 demibushel", "1 firkin"],
["2 firkins", "1 kilderkin"],
["2 kilderkins", "1 barrel"]]]']],
[[["map",[["2 chopins","1 pint"],["2 demibushel","1 firkin"],["2 firkins","1 kilderkin"],["2 gallons","1 peck"],["2 gills","1 chopin"],["2 kilderkins","1 barrel"],["2 pecks","1 demibushel"],["2 pints","1 quart"],["2 pottles","1 gallon"],["2 quarts","1 pottle"]]]]])
OVSDB_CHECK_NEGATIVE([duplicate integer key not allowed in map],
[[parse-data '{"key": "integer", "value": "boolean", "max": 5}' \
'["map", [[1, true], [2, false], [1, false]]]']],
[ovsdb error: map contains duplicate key])

321
tests/ovsdb-execution.at Normal file
View File

@@ -0,0 +1,321 @@
AT_BANNER([OVSDB -- execution])
m4_define([ORDINAL_SCHEMA],
[['{"name": "mydb",
"tables": {
"ordinals": {
"columns": {
"number": {"type": "integer"},
"name": {"type": "string"}}}}}']])
# This is like OVSDB_CHECK_POSITIVE, except that UUIDs in the output
# are replaced by markers of the form <N> where N is a number. The
# first unique UUID is replaced by <0>, the next by <1>, and so on.
# If a given UUID appears more than once it is always replaced by the
# same marker.
m4_define([OVSDB_CHECK_EXECUTION],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb execute execution positive $4])
OVS_CHECK_LCOV([test-ovsdb execute $2], [0], [stdout], [])
AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [$3])
AT_CLEANUP])
OVSDB_CHECK_EXECUTION([insert row, query table],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}}]' \
'[{"op": "select",
"table": "ordinals",
"where": []}]']],
[[[{"uuid":["uuid","<0>"]}]
[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, query by value],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}}]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}}]' \
'[{"op": "select",
"table": "ordinals",
"where": [["name", "==", "zero"]]}]'\
'[{"op": "select",
"table": "ordinals",
"where": [["name", "==", "one"]]}]']],
[[[{"uuid":["uuid","<0>"]}]
[{"uuid":["uuid","<1>"]}]
[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0}]}]
[{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, query by named-uuid],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"},
"uuid-name": "first"},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"},
"uuid-name": "second"},
{"op": "select",
"table": "ordinals",
"where": [["_uuid", "==", ["named-uuid", "first"]]]},
{"op": "select",
"table": "ordinals",
"where": [["_uuid", "==", ["named-uuid", "second"]]]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0}]},{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, update rows by value],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"},
"uuid-name": "first"}]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"},
"uuid-name": "first"}]' \
'[{"op": "update",
"table": "ordinals",
"where": [["name", "==", "zero"]],
"row": {"name": "nought"}}]' \
'[{"op": "select",
"table": "ordinals",
"where": [],
"sort": ["number"]}]']],
[[[{"uuid":["uuid","<0>"]}]
[{"uuid":["uuid","<1>"]}]
[{"count":1}]
[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"nought","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, delete by named-uuid],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"},
"uuid-name": "first"},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"},
"uuid-name": "second"},
{"op": "delete",
"table": "ordinals",
"where": [["_uuid", "==", ["named-uuid", "first"]]]},
{"op": "select",
"table": "ordinals",
"where": [],
"columns": ["name","number"]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"count":1},{"rows":[{"name":"one","number":1}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, delete rows by value],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"},
"uuid-name": "first"}]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"},
"uuid-name": "first"}]' \
'[{"op": "delete",
"table": "ordinals",
"where": [["name", "==", "zero"]]}]' \
'[{"op": "select",
"table": "ordinals",
"where": []}]']],
[[[{"uuid":["uuid","<0>"]}]
[{"uuid":["uuid","<1>"]}]
[{"count":1}]
[{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<2>"],"name":"one","number":1}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, delete by (non-matching) value],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"},
"uuid-name": "first"}]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"},
"uuid-name": "first"}]' \
'[{"op": "delete",
"table": "ordinals",
"where": [["name", "==", "nought"]]}]' \
'[{"op": "select",
"table": "ordinals",
"where": [],
"sort": ["number"]}]']],
[[[{"uuid":["uuid","<0>"]}]
[{"uuid":["uuid","<1>"]}]
[{"count":0}]
[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"zero","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
]])
OVSDB_CHECK_EXECUTION([insert rows, delete all],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"},
"uuid-name": "first"},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"},
"uuid-name": "second"},
{"op": "delete",
"table": "ordinals",
"where": []},
{"op": "select",
"table": "ordinals",
"where": [],
"columns": ["name","number"]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"count":2},{"rows":[]}]
]])
OVSDB_CHECK_EXECUTION([insert row, query table, commit],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "select",
"table": "ordinals",
"where": []},
{"op": "commit",
"durable": false}]']],
[[[{"uuid":["uuid","<0>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]},{}]
]])
OVSDB_CHECK_EXECUTION([insert row, query table, commit durably],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "select",
"table": "ordinals",
"where": []},
{"op": "commit",
"durable": true}]']],
[[[{"uuid":["uuid","<0>"]},{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<1>"],"name":"zero","number":0}]},{}]
]])
OVSDB_CHECK_EXECUTION([equality wait with correct rows],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 0,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1}]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
]])
OVSDB_CHECK_EXECUTION([equality wait with extra row],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 0,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1},
{"name": "two", "number": 2}]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out","error":"timed out"}]
]])
OVSDB_CHECK_EXECUTION([equality wait with missing row],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 0,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "one", "number": 1}]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out","error":"timed out"}]
]])
OVSDB_CHECK_EXECUTION([inequality wait with correct rows],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 0,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "!=",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1}]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out","error":"timed out"}]
]])
OVSDB_CHECK_EXECUTION([inequality wait with extra row],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 0,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "!=",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1},
{"name": "two", "number": 2}]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
]])
OVSDB_CHECK_EXECUTION([inequality wait with missing row],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 0,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "!=",
"rows": [{"name": "one", "number": 1}]}]']],
[[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{}]
]])

282
tests/ovsdb-file.at Normal file
View File

@@ -0,0 +1,282 @@
AT_BANNER([OVSDB -- file I/O])
AT_SETUP([create empty, reread])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_CREAT|O_RDWR'], [0],
[file: open successful
], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read], [0],
[file: open successful
file: read: end of file
], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write one, reread])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]']], [0],
[[file: open successful
file: write:[0] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read], [0],
[[file: open successful
file: read: [0]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([check that O_EXCL works])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[1]']], [0],
[[file: open successful
file: write:[1] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read], [0],
[[file: open successful
file: read: [1]
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_CREAT|O_RDWR|O_EXCL' read], [1],
[], [test-ovsdb: I/O error: create: file failed (File exists)
])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write one, reread])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write one, reread, append])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_RDWR' read read read 'write:["append"]']], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: write:["append"] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read: ["append"]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write, reread one, overwrite])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_RDWR' read 'write:["more data"]']], [0],
[[file: open successful
file: read: [0]
file: write:["more data"] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read], [0],
[[file: open successful
file: read: [0]
file: read: ["more data"]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write, add corrupted data, read])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
AT_CHECK([echo 'xxx' >> file])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read failed: syntax error: file: parse error at offset 174 in header line "xxx"
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write, add corrupted data, read, overwrite])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
AT_CHECK([echo 'xxx' >> file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_RDWR' read read read read 'write:[3]']], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read failed: syntax error: file: parse error at offset 174 in header line "xxx"
file: write:[3] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read: [3]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write, corrupt some data, read, overwrite])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
AT_CHECK([[sed 's/\[2]/[3]/' < file > file.tmp]])
AT_CHECK([mv file.tmp file])
AT_CHECK([[grep -c '\[3]' file]], [0], [1
])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_RDWR' read read read 'write:["longer data"]']], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read failed: syntax error: file: 4 bytes starting at offset 170 have SHA-1 hash 5c031e5c0d3a9338cc127ebe40bb2748b6a67e78 but should have hash 98f55556e7ffd432381b56a19bd485b3e6446442
file: write:["longer data"] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: ["longer data"]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write, truncate file, read, overwrite])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
AT_CHECK([[sed 's/\[2]/2/' < file > file.tmp]])
AT_CHECK([mv file.tmp file])
AT_CHECK([[grep -c '^2$' file]], [0], [1
])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_RDWR' read read read 'write:["longer data"]']], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read failed: I/O error: file: error reading 4 bytes starting at offset 170 (unexpected end of file)
file: write:["longer data"] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: ["longer data"]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP
AT_SETUP([write bad JSON, read, overwrite])
AT_KEYWORDS([ovsdb file])
AT_CAPTURE_FILE([file])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_CREAT|O_RDWR' 'write:[0]' 'write:[1]' 'write:[2]']], [0],
[[file: open successful
file: write:[0] successful
file: write:[1] successful
file: write:[2] successful
]], [ignore])
AT_CHECK([[printf '%s\n%s\n' 'OVSDB JSON 5 d910b02871075d3156ec8675dfc95b7d5d640aa6' 'null' >> file]])
OVS_CHECK_LCOV(
[[test-ovsdb file-io file 'O_RDWR' read read read read 'write:["replacement data"]']], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read failed: syntax error: file: 5 bytes starting at offset 228 are not valid JSON (syntax error at beginning of input)
file: write:["replacement data"] successful
]], [ignore])
OVS_CHECK_LCOV(
[test-ovsdb file-io file 'O_RDONLY' read read read read read], [0],
[[file: open successful
file: read: [0]
file: read: [1]
file: read: [2]
file: read: ["replacement data"]
file: read: end of file
]], [ignore])
AT_CHECK([test -f .file.~lock~])
AT_CLEANUP

535
tests/ovsdb-query.at Normal file
View File

@@ -0,0 +1,535 @@
AT_BANNER([OVSDB -- queries])
OVSDB_CHECK_POSITIVE([queries on scalars],
[[query \
'{"columns":
{"i": {"type": "integer"},
"r": {"type": "real"},
"b": {"type": "boolean"},
"s": {"type": "string"},
"u": {"type": "uuid"}}}' \
'[{"i": 0,
"r": 0.5,
"b": true,
"s": "a",
"u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]},
{"i": 1,
"r": 1.5,
"b": false,
"s": "b",
"u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]},
{"i": 2,
"r": 2.5,
"b": true,
"s": "c",
"u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]},
{"i": 3,
"r": 3.5,
"b": false,
"s": "d",
"u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]},
{"i": 4,
"r": 4.5,
"b": true,
"s": "e",
"u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \
'[[],
[["i", "==", 0]],
[["i", "!=", 1]],
[["i", "<", 2]],
[["i", "<=", 3]],
[["i", ">", 2]],
[["i", ">=", 4]],
[["i", "includes", 3]],
[["i", "excludes", 2]],
[["r", "==", 0.5]],
[["r", "!=", 1.5]],
[["r", "<", 2.5]],
[["r", "<=", 3.5]],
[["r", ">", 4.5]],
[["r", ">=", 5.5]],
[["r", "includes", 1]],
[["r", "excludes", 3]],
[["b", "==", true]],
[["b", "!=", true]],
[["b", "includes", false]],
[["b", "excludes", true]],
[["s", "==", "a"]],
[["s", "!=", "b"]],
[["s", "includes", "c"]],
[["s", "excludes", "d"]],
[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]],
[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]],
[["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]']],
[dnl
query 0: 11111
query 1: 1----
query 2: 1-111
query 3: 11---
query 4: 1111-
query 5: ---11
query 6: ----1
query 7: ---1-
query 8: 11-11
query 9: 1----
query 10: 1-111
query 11: 11---
query 12: 1111-
query 13: -----
query 14: -----
query 15: -----
query 16: 11111
query 17: 1-1-1
query 18: -1-1-
query 19: -1-1-
query 20: -1-1-
query 21: 1----
query 22: 1-111
query 23: --1--
query 24: 111-1
query 25: 1----
query 26: 1-111
query 27: --1--],
[query])
OVSDB_CHECK_POSITIVE([queries on sets],
[[query \
'{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \
'[{"i": ["set", []]},
{"i": ["set", [0]]},
{"i": ["set", [1]]},
{"i": ["set", [0, 1]]},
{"i": ["set", [2]]},
{"i": ["set", [2, 0]]},
{"i": ["set", [2, 1]]},
{"i": ["set", [2, 1, 0]]}]' \
'[[],
[["i", "==", ["set", []]]],
[["i", "==", ["set", [0]]]],
[["i", "==", ["set", [1]]]],
[["i", "==", ["set", [0, 1]]]],
[["i", "==", ["set", [2]]]],
[["i", "==", ["set", [2, 0]]]],
[["i", "==", ["set", [2, 1]]]],
[["i", "==", ["set", [2, 1, 0]]]],
[["i", "!=", ["set", []]]],
[["i", "!=", ["set", [0]]]],
[["i", "!=", ["set", [1]]]],
[["i", "!=", ["set", [0, 1]]]],
[["i", "!=", ["set", [2]]]],
[["i", "!=", ["set", [2, 0]]]],
[["i", "!=", ["set", [2, 1]]]],
[["i", "!=", ["set", [2, 1, 0]]]],
[["i", "includes", ["set", []]]],
[["i", "includes", ["set", [0]]]],
[["i", "includes", ["set", [1]]]],
[["i", "includes", ["set", [0, 1]]]],
[["i", "includes", ["set", [2]]]],
[["i", "includes", ["set", [2, 0]]]],
[["i", "includes", ["set", [2, 1]]]],
[["i", "includes", ["set", [2, 1, 0]]]],
[["i", "excludes", ["set", []]]],
[["i", "excludes", ["set", [0]]]],
[["i", "excludes", ["set", [1]]]],
[["i", "excludes", ["set", [0, 1]]]],
[["i", "excludes", ["set", [2]]]],
[["i", "excludes", ["set", [2, 0]]]],
[["i", "excludes", ["set", [2, 1]]]],
[["i", "excludes", ["set", [2, 1, 0]]]]]']],
[dnl
query 0: 11111 111
query 1: 1---- ---
query 2: -1--- ---
query 3: --1-- ---
query 4: ---1- ---
query 5: ----1 ---
query 6: ----- 1--
query 7: ----- -1-
query 8: ----- --1
query 9: -1111 111
query 10: 1-111 111
query 11: 11-11 111
query 12: 111-1 111
query 13: 1111- 111
query 14: 11111 -11
query 15: 11111 1-1
query 16: 11111 11-
query 17: 11111 111
query 18: -1-1- 1-1
query 19: --11- -11
query 20: ---1- --1
query 21: ----1 111
query 22: ----- 1-1
query 23: ----- -11
query 24: ----- --1
query 25: 11111 111
query 26: 1-1-1 -1-
query 27: 11--1 1--
query 28: 1---1 ---
query 29: 1111- ---
query 30: 1-1-- ---
query 31: 11--- ---
query 32: 1---- ---], [query])
# This is the same as the "set" test except that it adds values,
# all of which always match.
OVSDB_CHECK_POSITIVE([queries on maps (1)],
[[query \
'{"columns": {"i": {"type": {"key": "integer",
"value": "boolean",
"min": 0,
"max": "unlimited"}}}}' \
'[{"i": ["map", []]},
{"i": ["map", [[0, true]]]},
{"i": ["map", [[1, false]]]},
{"i": ["map", [[0, true], [1, false]]]},
{"i": ["map", [[2, true]]]},
{"i": ["map", [[2, true], [0, true]]]},
{"i": ["map", [[2, true], [1, false]]]},
{"i": ["map", [[2, true], [1, false], [0, true]]]}]' \
'[[],
[["i", "==", ["map", []]]],
[["i", "==", ["map", [[0, true]]]]],
[["i", "==", ["map", [[1, false]]]]],
[["i", "==", ["map", [[0, true], [1, false]]]]],
[["i", "==", ["map", [[2, true]]]]],
[["i", "==", ["map", [[2, true], [0, true]]]]],
[["i", "==", ["map", [[2, true], [1, false]]]]],
[["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "!=", ["map", []]]],
[["i", "!=", ["map", [[0, true]]]]],
[["i", "!=", ["map", [[1, false]]]]],
[["i", "!=", ["map", [[0, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true]]]]],
[["i", "!=", ["map", [[2, true], [0, true]]]]],
[["i", "!=", ["map", [[2, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "includes", ["map", []]]],
[["i", "includes", ["map", [[0, true]]]]],
[["i", "includes", ["map", [[1, false]]]]],
[["i", "includes", ["map", [[0, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true]]]]],
[["i", "includes", ["map", [[2, true], [0, true]]]]],
[["i", "includes", ["map", [[2, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "excludes", ["map", []]]],
[["i", "excludes", ["map", [[0, true]]]]],
[["i", "excludes", ["map", [[1, false]]]]],
[["i", "excludes", ["map", [[0, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true]]]]],
[["i", "excludes", ["map", [[2, true], [0, true]]]]],
[["i", "excludes", ["map", [[2, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]']],
[dnl
query 0: 11111 111
query 1: 1---- ---
query 2: -1--- ---
query 3: --1-- ---
query 4: ---1- ---
query 5: ----1 ---
query 6: ----- 1--
query 7: ----- -1-
query 8: ----- --1
query 9: -1111 111
query 10: 1-111 111
query 11: 11-11 111
query 12: 111-1 111
query 13: 1111- 111
query 14: 11111 -11
query 15: 11111 1-1
query 16: 11111 11-
query 17: 11111 111
query 18: -1-1- 1-1
query 19: --11- -11
query 20: ---1- --1
query 21: ----1 111
query 22: ----- 1-1
query 23: ----- -11
query 24: ----- --1
query 25: 11111 111
query 26: 1-1-1 -1-
query 27: 11--1 1--
query 28: 1---1 ---
query 29: 1111- ---
query 30: 1-1-- ---
query 31: 11--- ---
query 32: 1---- ---], [query])
# This is the same as the "set" test except that it adds values,
# and those values don't always match.
OVSDB_CHECK_POSITIVE([queries on maps (2)],
[[query \
'{"columns": {"i": {"type": {"key": "integer",
"value": "boolean",
"min": 0,
"max": "unlimited"}}}}' \
'[{"i": ["map", []]},
{"i": ["map", [[0, true]]]},
{"i": ["map", [[0, false]]]},
{"i": ["map", [[1, false]]]},
{"i": ["map", [[1, true]]]},
{"i": ["map", [[0, true], [1, false]]]},
{"i": ["map", [[0, true], [1, true]]]},
{"i": ["map", [[2, true]]]},
{"i": ["map", [[2, false]]]},
{"i": ["map", [[2, true], [0, true]]]},
{"i": ["map", [[2, false], [0, true]]]},
{"i": ["map", [[2, true], [1, false]]]},
{"i": ["map", [[2, true], [1, true]]]},
{"i": ["map", [[2, true], [1, false], [0, true]]]},
{"i": ["map", [[2, true], [1, false], [0, false]]]}]' \
'[[],
[["i", "==", ["map", []]]],
[["i", "==", ["map", [[0, true]]]]],
[["i", "==", ["map", [[1, false]]]]],
[["i", "==", ["map", [[0, true], [1, false]]]]],
[["i", "==", ["map", [[2, true]]]]],
[["i", "==", ["map", [[2, true], [0, true]]]]],
[["i", "==", ["map", [[2, true], [1, false]]]]],
[["i", "==", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "!=", ["map", []]]],
[["i", "!=", ["map", [[0, true]]]]],
[["i", "!=", ["map", [[1, false]]]]],
[["i", "!=", ["map", [[0, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true]]]]],
[["i", "!=", ["map", [[2, true], [0, true]]]]],
[["i", "!=", ["map", [[2, true], [1, false]]]]],
[["i", "!=", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "includes", ["map", []]]],
[["i", "includes", ["map", [[0, true]]]]],
[["i", "includes", ["map", [[1, false]]]]],
[["i", "includes", ["map", [[0, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true]]]]],
[["i", "includes", ["map", [[2, true], [0, true]]]]],
[["i", "includes", ["map", [[2, true], [1, false]]]]],
[["i", "includes", ["map", [[2, true], [1, false], [0, true]]]]],
[["i", "excludes", ["map", []]]],
[["i", "excludes", ["map", [[0, true]]]]],
[["i", "excludes", ["map", [[1, false]]]]],
[["i", "excludes", ["map", [[0, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true]]]]],
[["i", "excludes", ["map", [[2, true], [0, true]]]]],
[["i", "excludes", ["map", [[2, true], [1, false]]]]],
[["i", "excludes", ["map", [[2, true], [1, false], [0, true]]]]]]']],
[dnl
query 0: 11111 11111 11111
query 1: 1---- ----- -----
query 2: -1--- ----- -----
query 3: ---1- ----- -----
query 4: ----- 1---- -----
query 5: ----- --1-- -----
query 6: ----- ----1 -----
query 7: ----- ----- -1---
query 8: ----- ----- ---1-
query 9: -1111 11111 11111
query 10: 1-111 11111 11111
query 11: 111-1 11111 11111
query 12: 11111 -1111 11111
query 13: 11111 11-11 11111
query 14: 11111 1111- 11111
query 15: 11111 11111 1-111
query 16: 11111 11111 111-1
query 17: 11111 11111 11111
query 18: -1--- 11--1 1--1-
query 19: ---1- 1---- -1-11
query 20: ----- 1---- ---1-
query 21: ----- --1-1 -1111
query 22: ----- ----1 ---1-
query 23: ----- ----- -1-11
query 24: ----- ----- ---1-
query 25: 11111 11111 11111
query 26: 1-111 --11- -11-1
query 27: 111-1 -1111 1-1--
query 28: 1-1-1 --11- --1--
query 29: 11111 11-1- 1----
query 30: 1-111 ---1- -----
query 31: 111-1 -1-1- 1----
query 32: 1-1-1 ---1- -----], [query])
OVSDB_CHECK_POSITIVE([UUID-distinct queries on scalars],
[[query-distinct \
'{"columns":
{"i": {"type": "integer"},
"r": {"type": "real"},
"b": {"type": "boolean"},
"s": {"type": "string"},
"u": {"type": "uuid"}}}' \
'[{"i": 0,
"r": 0.5,
"b": true,
"s": "a",
"u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]},
{"i": 1,
"r": 1.5,
"b": false,
"s": "b",
"u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]},
{"i": 2,
"r": 2.5,
"b": true,
"s": "c",
"u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]},
{"i": 3,
"r": 3.5,
"b": false,
"s": "d",
"u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]},
{"i": 4,
"r": 4.5,
"b": true,
"s": "e",
"u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \
'[[],
[["i", "==", 0]],
[["i", "!=", 1]],
[["i", "<", 2]],
[["i", "<=", 3]],
[["i", ">", 2]],
[["i", ">=", 4]],
[["i", "includes", 3]],
[["i", "excludes", 2]],
[["r", "==", 0.5]],
[["r", "!=", 1.5]],
[["r", "<", 2.5]],
[["r", "<=", 3.5]],
[["r", ">", 4.5]],
[["r", ">=", 5.5]],
[["r", "includes", 1]],
[["r", "excludes", 3]],
[["b", "==", true]],
[["b", "!=", true]],
[["b", "includes", false]],
[["b", "excludes", true]],
[["s", "==", "a"]],
[["s", "!=", "b"]],
[["s", "includes", "c"]],
[["s", "excludes", "d"]],
[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]],
[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]],
[["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]' \
'["_uuid"]']],
[dnl
query 0: abcde
query 1: a----
query 2: a-cde
query 3: ab---
query 4: abcd-
query 5: ---de
query 6: ----e
query 7: ---d-
query 8: ab-de
query 9: a----
query 10: a-cde
query 11: ab---
query 12: abcd-
query 13: -----
query 14: -----
query 15: -----
query 16: abcde
query 17: a-c-e
query 18: -b-d-
query 19: -b-d-
query 20: -b-d-
query 21: a----
query 22: a-cde
query 23: --c--
query 24: abc-e
query 25: a----
query 26: a-cde
query 27: --c--],
[query])
OVSDB_CHECK_POSITIVE([Boolean-distinct queries on scalars],
[[query-distinct \
'{"columns":
{"i": {"type": "integer"},
"r": {"type": "real"},
"b": {"type": "boolean"},
"s": {"type": "string"},
"u": {"type": "uuid"}}}' \
'[{"i": 0,
"r": 0.5,
"b": true,
"s": "a",
"u": ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]},
{"i": 1,
"r": 1.5,
"b": false,
"s": "b",
"u": ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]},
{"i": 2,
"r": 2.5,
"b": true,
"s": "c",
"u": ["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]},
{"i": 3,
"r": 3.5,
"b": false,
"s": "d",
"u": ["uuid", "62315898-64e0-40b9-b26f-ff74225303e6"]},
{"i": 4,
"r": 4.5,
"b": true,
"s": "e",
"u": ["uuid", "4a5127e2-0256-4a72-a7dc-6246213967c7"]}]' \
'[[],
[["i", "==", 0]],
[["i", "!=", 1]],
[["i", "<", 2]],
[["i", "<=", 3]],
[["i", ">", 2]],
[["i", ">=", 4]],
[["i", "includes", 3]],
[["i", "excludes", 2]],
[["r", "==", 0.5]],
[["r", "!=", 1.5]],
[["r", "<", 2.5]],
[["r", "<=", 3.5]],
[["r", ">", 4.5]],
[["r", ">=", 5.5]],
[["r", "includes", 1]],
[["r", "excludes", 3]],
[["b", "==", true]],
[["b", "!=", true]],
[["b", "includes", false]],
[["b", "excludes", true]],
[["s", "==", "a"]],
[["s", "!=", "b"]],
[["s", "includes", "c"]],
[["s", "excludes", "d"]],
[["u", "==", ["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]],
[["u", "!=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]],
[["u", "includes",["uuid", "ad0fa355-8b84-4a36-a4b5-b2c1bfd91758"]]]]' \
'["b"]']],
[dnl
query 0: ababa
query 1: a-a-a
query 2: ababa
query 3: ababa
query 4: ababa
query 5: ababa
query 6: a-a-a
query 7: -b-b-
query 8: ababa
query 9: a-a-a
query 10: ababa
query 11: ababa
query 12: ababa
query 13: -----
query 14: -----
query 15: -----
query 16: ababa
query 17: a-a-a
query 18: -b-b-
query 19: -b-b-
query 20: -b-b-
query 21: a-a-a
query 22: ababa
query 23: a-a-a
query 24: ababa
query 25: a-a-a
query 26: ababa
query 27: a-a-a],
[query])

277
tests/ovsdb-row.at Normal file
View File

@@ -0,0 +1,277 @@
AT_BANNER([OVSDB -- rows])
# Autoconf 2.63 has a bug that causes the double-quotes below to be
# lost, so that the following tests fail, so we mark them as XFAIL for
# Autoconf < 2.64.
m4_define([RESERVED_COLUMNS], [["_uuid":["uuid","00000000-0000-0000-0000-000000000000"],"_version":["uuid","00000000-0000-0000-0000-000000000000"]]])
OVSDB_CHECK_POSITIVE([row with one string column],
[[parse-rows \
'{"columns": {"name": {"type": "string"}}}' \
'{"name": "value"}' \
'{"name": ""}' \
'{"name": "longer string with spaces"}' \
'{}']],
[{RESERVED_COLUMNS,"name":"value"}
name
{RESERVED_COLUMNS,"name":""}
name
{RESERVED_COLUMNS,"name":"longer string with spaces"}
name
{RESERVED_COLUMNS,"name":""}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row with one integer column],
[[parse-rows \
'{"columns": {"count": {"type": "integer"}}}' \
'{"count": 1}' \
'{"count": -1}' \
'{"count": 2e10}' \
'{}']],
[{RESERVED_COLUMNS,"count":1}
count
{RESERVED_COLUMNS,"count":-1}
count
{RESERVED_COLUMNS,"count":20000000000}
count
{RESERVED_COLUMNS,"count":0}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row with one real column],
[[parse-rows \
'{"columns": {"cost": {"type": "real"}}}' \
'{"cost": 1.0}' \
'{"cost": -2.0}' \
'{"cost": 123000}' \
'{}']],
[{RESERVED_COLUMNS,"cost":1}
cost
{RESERVED_COLUMNS,"cost":-2}
cost
{RESERVED_COLUMNS,"cost":123000}
cost
{RESERVED_COLUMNS,"cost":0}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row with one boolean column],
[[parse-rows \
'{"columns": {"feasible": {"type": "boolean"}}}' \
'{"feasible": true}' \
'{"feasible": false}' \
'{}']],
[{RESERVED_COLUMNS,"feasible":true}
feasible
{RESERVED_COLUMNS,"feasible":false}
feasible
{RESERVED_COLUMNS,"feasible":false}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row with one uuid column],
[[parse-rows \
'{"columns": {"ref": {"type": "uuid"}}}' \
'{"ref": ["uuid", "f707423d-bf5b-48b5-b6c0-797c900ba4b6"]}' \
'{"ref": ["uuid", "33583cc5-d2f4-43de-b1ca-8aac14071b51"]}' \
'{}']],
[{RESERVED_COLUMNS,"ref":[["uuid","f707423d-bf5b-48b5-b6c0-797c900ba4b6"]]}
ref
{RESERVED_COLUMNS,"ref":[["uuid","33583cc5-d2f4-43de-b1ca-8aac14071b51"]]}
ref
{RESERVED_COLUMNS,"ref":[["uuid","00000000-0000-0000-0000-000000000000"]]}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row with set of 1 to 2 elements],
[[parse-rows \
'{"columns": {"myset": {"type": {"key": "integer", "min": 1, "max": 2}}}}' \
'{}']],
[{RESERVED_COLUMNS,["myset":["set",[0]]]}
<none>])
OVSDB_CHECK_POSITIVE([row with map of 1 to 2 elements],
[[parse-rows \
'{"columns": {"mymap": {"type": {"key": "integer", "value": "uuid", "min": 1, "max": 2}}}}' \
'{}']],
[{RESERVED_COLUMNS,["mymap":["map",[[0,["uuid","00000000-0000-0000-0000-000000000000"]]]]]}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row with several columns],
[[parse-rows \
'{"columns":
{"vswitch": {"type": "uuid"},
"name": {"type": "string"},
"datapath_id": {"type": {"key": "string", "min": 0}},
"hwaddr": {"type": "string"},
"mirrors": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
"netflows": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
"controller": {"type": {"key": "uuid", "min": 0}},
"listeners": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}},
"snoops": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
'{"vswitch": ["uuid", "1a5c7280-0d4c-4e34-9ec7-c772339f7774"],
"name": "br0",
"datapath_id": ["set", ["000ae4256bb0"]],
"hwaddr": "00:0a:e4:25:6b:b0"}' \
'{}']],
[{RESERVED_COLUMNS,["controller":["set",[]],"datapath_id":["set",["000ae4256bb0"]],"hwaddr":"00:0a:e4:25:6b:b0","listeners":["set",[]],"mirrors":["set",[]],"name":"br0","netflows":["set",[]],"snoops":["set",[]],"vswitch":["uuid","1a5c7280-0d4c-4e34-9ec7-c772339f7774"]]}
datapath_id, hwaddr, name, vswitch
{RESERVED_COLUMNS,["controller":["set",[]],"datapath_id":["set",[]],"hwaddr":"","listeners":["set",[]],"mirrors":["set",[]],"name":"","netflows":["set",[]],"snoops":["set",[]],"vswitch":["uuid","00000000-0000-0000-0000-000000000000"]]}
<none>], [], [2.64])
OVSDB_CHECK_POSITIVE([row hashing (scalars)],
[[compare-rows \
'{"columns":
{"i": {"type": "integer"},
"r": {"type": "real"},
"b": {"type": "boolean"},
"s": {"type": "string"},
"u": {"type": "uuid"}}}' \
'["null", {}]' \
'["i1", {"i": 1}]' \
'["i2", {"i": 2}]' \
'["i4", {"i": 4}]' \
'["i8", {"i": 8}]' \
'["i16", {"i": 16}]' \
'["i32", {"i": 32}]' \
'["i64", {"i": 64}]' \
'["i128", {"i": 128}]' \
'["i256", {"i": 256}]' \
'["null2", {"r": -0}]' \
'["r123", {"r": 123}]' \
'["r0.0625", {"r": 0.0625}]' \
'["r0.125", {"r": 0.125}]' \
'["r0.25", {"r": 0.25}]' \
'["r0.5", {"r": 0.5}]' \
'["r1", {"r": 1}]' \
'["r2", {"r": 2}]' \
'["r4", {"r": 4}]' \
'["r8", {"r": 8}]' \
'["r16", {"r": 16}]' \
'["r32", {"r": 32}]' \
'["null3", {"b": false}]' \
'["b1", {"b": true}]' \
'["null4", {"s": ""}]' \
'["s0", {"s": "a"}]' \
'["s1", {"s": "b"}]' \
'["s2", {"s": "c"}]' \
'["s3", {"s": "d"}]' \
'["s4", {"s": "e"}]' \
'["s5", {"s": "f"}]' \
'["s6", {"s": "g"}]' \
'["s7", {"s": "h"}]' \
'["s8", {"s": "i"}]' \
'["s9", {"s": "j"}]' \
'["null5", {"u": ["uuid","00000000-0000-0000-0000-000000000000"]}]' \
'["u1", {"u": ["uuid","10000000-0000-0000-0000-000000000000"]}]' \
'["u2", {"u": ["uuid","01000000-0000-0000-0000-000000000000"]}]' \
'["u3", {"u": ["uuid","00100000-0000-0000-0000-000000000000"]}]' \
'["u4", {"u": ["uuid","00010000-0000-0000-0000-000000000000"]}]' \
'["u5", {"u": ["uuid","00001000-0000-0000-0000-000000000000"]}]' \
'["u6", {"u": ["uuid","00000100-0000-0000-0000-000000000000"]}]' \
'["u7", {"u": ["uuid","00000010-0000-0000-0000-000000000000"]}]' \
'["u8", {"u": ["uuid","00000001-0000-0000-0000-000000000000"]}]' \
'["null6", {"u": ["uuid","00000000-c6db-4d22-970f-b41fabd20c4b"]}]']],
[[null == null2
null == null3
null == null4
null == null5
hash(null) == hash(null6)
null2 == null3
null2 == null4
null2 == null5
hash(null2) == hash(null6)
null3 == null4
null3 == null5
hash(null3) == hash(null6)
null4 == null5
hash(null4) == hash(null6)
hash(null5) == hash(null6)]])
OVSDB_CHECK_POSITIVE([row hashing (sets)],
[[compare-rows \
'{"columns":
{"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
"r": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
"b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
"s": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
"u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
'["null0", {"i": ["set", []]}]' \
'["i0", {"i": ["set", [0]]}]' \
'["i01", {"i": ["set", [0, 1]]}]' \
'["i012", {"i": ["set", [0, 1, 2]]}]' \
'["i021", {"i": ["set", [0, 2, 1]]}]' \
'["i201", {"i": ["set", [2, 0, 1]]}]' \
'["i102", {"i": ["set", [1, 0, 2]]}]' \
'["i120", {"i": ["set", [1, 2, 0]]}]' \
'["i210", {"i": ["set", [2, 1, 0]]}]' \
'["r0", {"r": ["set", [0]]}]' \
'["r01", {"r": ["set", [0, 1]]}]' \
'["r012", {"r": ["set", [0, 1, 2]]}]' \
'["r201", {"r": ["set", [2, 0, 1]]}]' \
'["null1", {"b": ["set", []]}]' \
'["b0", {"b": ["set", [false]]}]' \
'["b1", {"b": ["set", [true]]}]' \
'["b01", {"b": ["set", [false, true]]}]' \
'["b10", {"b": ["set", [true, false]]}]' \
'["null2", {"s": ["set", []]}]' \
'["sa", {"s": ["set", ["a"]]}]' \
'["sb", {"s": ["set", ["b"]]}]' \
'["sab", {"s": ["set", ["a", "b"]]}]' \
'["sba", {"s": ["set", ["b", "a"]]}]']],
[[null0 == null1
null0 == null2
i012 == i021
i012 == i201
i012 == i102
i012 == i120
i012 == i210
i021 == i201
i021 == i102
i021 == i120
i021 == i210
i201 == i102
i201 == i120
i201 == i210
i102 == i120
i102 == i210
i120 == i210
r012 == r201
null1 == null2
b01 == b10
sab == sba]])
OVSDB_CHECK_POSITIVE([row hashing (maps)],
[[compare-rows \
'{"columns":
{"ii": {"type": {"key": "integer", "value": "integer",
"min": 0, "max": "unlimited"}},
"rr": {"type": {"key": "real", "value": "real",
"min": 0, "max": "unlimited"}},
"bb": {"type": {"key": "boolean", "value": "boolean",
"min": 0, "max": "unlimited"}},
"ss": {"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}}}' \
'["null", {}]' \
'["ii0", {"ii": ["map", [[0, 0]]]}]' \
'["ii1", {"ii": ["map", [[0, 1]]]}]' \
'["ii00", {"ii": ["map", [[0, 0], [1, 0]]]}]' \
'["ii01", {"ii": ["map", [[0, 0], [1, 1]]]}]' \
'["ii10", {"ii": ["map", [[0, 1], [1, 0]]]}]' \
'["ii11", {"ii": ["map", [[0, 1], [1, 1]]]}]' \
'["rr0", {"rr": ["map", [[0, 0]]]}]' \
'["rr0", {"rr": ["map", [[0, 1]]]}]' \
'["rr00", {"rr": ["map", [[0, 0], [1, 0]]]}]' \
'["rr01", {"rr": ["map", [[0, 0], [1, 1]]]}]' \
'["rr10", {"rr": ["map", [[0, 1], [1, 0]]]}]' \
'["rr11", {"rr": ["map", [[0, 1], [1, 1]]]}]' \
'["bb0", {"bb": ["map", [[false, false]]]}]' \
'["bb1", {"bb": ["map", [[false, true]]]}]' \
'["bb00", {"bb": ["map", [[false, false], [true, false]]]}]' \
'["bb01", {"bb": ["map", [[false, false], [true, true]]]}]' \
'["bb10", {"bb": ["map", [[false, true], [true, false]]]}]' \
'["bb11", {"bb": ["map", [[false, true], [true, true]]]}]' \
'["ss0", {"ss": ["map", [["a", "a"]]]}]' \
'["ss1", {"ss": ["map", [["a", "b"]]]}]' \
'["ss00", {"ss": ["map", [["a", "a"], ["b", "a"]]]}]' \
'["ss01", {"ss": ["map", [["a", "a"], ["b", "b"]]]}]' \
'["ss10", {"ss": ["map", [["a", "b"], ["b", "a"]]]}]' \
'["ss11", {"ss": ["map", [["a", "b"], ["b", "b"]]]}]'; echo
]], [[]])

31
tests/ovsdb-table.at Normal file
View File

@@ -0,0 +1,31 @@
AT_BANNER([OVSDB -- tables])
OVSDB_CHECK_POSITIVE([table with one column],
[[parse-table mytable '{"columns": {"name": {"type": "string"}}}']],
[[{"columns":{"name":{"type":"string"}}}]])
OVSDB_CHECK_POSITIVE([immutable table with one column],
[[parse-table mytable \
'{"columns": {"name": {"type": "string"}},
"mutable": false}']],
[[{"columns":{"name":{"type":"string"}},"mutable":false}]])
OVSDB_CHECK_POSITIVE([table with comment],
[[parse-table mytable \
'{"columns": {"name": {"type": "string"}},
"comment": "description of table"}']],
[[{"columns":{"name":{"type":"string"}},"comment":"description of table"}]])
OVSDB_CHECK_NEGATIVE([column names may not begin with _],
[[parse-table mytable \
'{"columns": {"_column": {"type": "integer"}}}']],
[[names beginning with "_" are reserved]],
[table])
OVSDB_CHECK_NEGATIVE([table must have at least one column (1)],
[[parse-table mytable '{}']],
[[Parsing table schema for table mytable failed: Required 'columns' member is missing.]])
OVSDB_CHECK_NEGATIVE([table must have at least one column (2)],
[[parse-table mytable '{"columns": {}}']],
[[table must have at least one column]])

384
tests/ovsdb-transaction.at Normal file
View File

@@ -0,0 +1,384 @@
AT_BANNER([OVSDB -- transactions])
OVSDB_CHECK_POSITIVE([empty table, empty transaction],
[[transact \
'["print"]' \
'["commit"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
print:
commit:
print:
abort:
print:])
OVSDB_CHECK_POSITIVE([nonempty table, empty transaction],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["print"]' \
'["commit"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
print:
1: i=2, j=3
2: i=2, j=3
commit:
print:
1: i=2, j=3
2: i=2, j=3
abort:
print:
1: i=2, j=3
2: i=2, j=3])
OVSDB_CHECK_POSITIVE([insert, commit],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["insert", "3", "1", "2"]' \
'["print"]' \
'["commit"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
insert 3 1 2:
print:
1: i=2, j=3
2: i=2, j=3
3: i=1, j=2
commit:
print:
1: i=2, j=3
2: i=2, j=3
3: i=1, j=2],
[transaction])
OVSDB_CHECK_POSITIVE([insert, abort],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["insert", "3", "1", "2"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
insert 3 1 2:
print:
1: i=2, j=3
2: i=2, j=3
3: i=1, j=2
abort:
print:
1: i=2, j=3
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([modify, commit],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["modify", "2", "5", "-1"]' \
'["modify", "1", "-1", "4"]' \
'["print"]' \
'["commit"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
modify 2 5 -1:
modify 1 -1 4:
print:
1: i=2, j=4
2: i=5, j=3
commit:
print:
1: i=2, j=4
2: i=5, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([modify, abort],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["modify", "2", "5", "-1"]' \
'["modify", "1", "-1", "4"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
modify 2 5 -1:
modify 1 -1 4:
print:
1: i=2, j=4
2: i=5, j=3
abort:
print:
1: i=2, j=3
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([delete, commit],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["delete", "1"]' \
'["print"]' \
'["commit"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
delete 1:
print:
2: i=2, j=3
commit:
print:
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([delete, abort],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["delete", "1"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
delete 1:
print:
2: i=2, j=3
abort:
print:
1: i=2, j=3
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([modify, delete, commit],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["modify", "1", "5", "6"]' \
'["delete", "1"]' \
'["print"]' \
'["commit"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
modify 1 5 6:
delete 1:
print:
2: i=2, j=3
commit:
print:
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([modify, delete, abort],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["modify", "1", "5", "6"]' \
'["delete", "1"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
modify 1 5 6:
delete 1:
print:
2: i=2, j=3
abort:
print:
1: i=2, j=3
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([insert, delete, commit],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["insert", "3", "5", "6"]' \
'["delete", "1"]' \
'["delete", "3"]' \
'["print"]' \
'["commit"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
insert 3 5 6:
delete 1:
delete 3:
print:
2: i=2, j=3
commit:
print:
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([insert, delete, abort],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["insert", "3", "5", "6"]' \
'["delete", "1"]' \
'["delete", "3"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
insert 3 5 6:
delete 1:
delete 3:
print:
2: i=2, j=3
abort:
print:
1: i=2, j=3
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([insert, modify, delete, commit],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["insert", "3", "5", "6"]' \
'["delete", "1"]' \
'["modify", "3", "7", "8"]' \
'["delete", "3"]' \
'["print"]' \
'["commit"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
insert 3 5 6:
delete 1:
modify 3 7 8:
delete 3:
print:
2: i=2, j=3
commit:
print:
2: i=2, j=3],
[transaction])
OVSDB_CHECK_POSITIVE([insert, modify, delete, abort],
[[transact \
'["insert", "1", "2", "3"]' \
'["insert", "2", "2", "3"]' \
'["commit"]' \
'["print"]' \
'["insert", "3", "5", "6"]' \
'["delete", "1"]' \
'["modify", "3", "7", "8"]' \
'["delete", "3"]' \
'["print"]' \
'["abort"]' \
'["print"]']],
[dnl
insert 1 2 3:
insert 2 2 3:
commit:
print:
1: i=2, j=3
2: i=2, j=3
insert 3 5 6:
delete 1:
modify 3 7 8:
delete 3:
print:
2: i=2, j=3
abort:
print:
1: i=2, j=3
2: i=2, j=3],
[transaction])

173
tests/ovsdb-trigger.at Normal file
View File

@@ -0,0 +1,173 @@
AT_BANNER([OVSDB -- triggers])
# This is like OVSDB_CHECK_POSITIVE, except that UUIDs in the output
# are replaced by markers of the form <N> where N is a number. The
# first unique UUID is replaced by <0>, the next by <1>, and so on.
# If a given UUID appears more than once it is always replaced by the
# same marker.
m4_define([OVSDB_CHECK_TRIGGER],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb execute execution trigger positive $4])
OVS_CHECK_LCOV([test-ovsdb trigger $2], [0], [stdout], [])
AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [$3])
AT_CLEANUP])
OVSDB_CHECK_TRIGGER([trigger fires immediately],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 10,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1}]},
{"op": "insert",
"table": "ordinals",
"row": {"number": 2, "name": "two"}}]']],
[[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{},{"uuid":["uuid","<2>"]}]
]])
OVSDB_CHECK_TRIGGER([trigger times out],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "wait",
"timeout": 10,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1},
{"name": "two", "number": 2}]}]' \
'["advance", 10]']],
[[t=0: new trigger 0
t=10: trigger 0 (delayed): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"details":"\"wait\" timed out after 10 ms","error":"timed out"}]
]])
OVSDB_CHECK_TRIGGER([trigger fires after delay],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}}]' \
'["advance", 5]' \
'[{"op": "wait",
"timeout": 10,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1},
{"name": "two", "number": 2}]}]' \
'["advance", 5]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 2, "name": "two"}}]']],
[[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]
t=5: new trigger 1
t=10: trigger 2 (immediate): [{"uuid":["uuid","<2>"]}]
t=10: trigger 1 (delayed): [{}]
]])
OVSDB_CHECK_TRIGGER([delayed trigger modifies database],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}}]' \
'["advance", 5]' \
'[{"op": "wait",
"timeout": 10,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1},
{"name": "two", "number": 2}]},
{"op": "delete",
"table": "ordinals",
"where": [["number", "<", 2]]}]' \
'["advance", 5]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 2, "name": "two"}}]' \
'["advance", 5]' \
'[{"op": "select",
"table": "ordinals",
"where": []}]']],
[[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]
t=5: new trigger 1
t=10: trigger 2 (immediate): [{"uuid":["uuid","<2>"]}]
t=10: trigger 1 (delayed): [{},{"count":2}]
t=15: trigger 3 (immediate): [{"rows":[{"_uuid":["uuid","<2>"],"_version":["uuid","<3>"],"name":"two","number":2}]}]
]])
OVSDB_CHECK_TRIGGER([one delayed trigger wakes up another],
[ORDINAL_SCHEMA [\
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}}]' \
'["advance", 5]' \
'[{"op": "wait",
"timeout": 10,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "two", "number": 2}]},
{"op": "delete",
"table": "ordinals",
"where": [["number", "==", 2]]},
{"op": "insert",
"table": "ordinals",
"row": {"number": 3, "name": "three"}}]' \
'[{"op": "wait",
"timeout": 10,
"table": "ordinals",
"where": [],
"columns": ["name", "number"],
"until": "==",
"rows": [{"name": "zero", "number": 0},
{"name": "one", "number": 1},
{"name": "two", "number": 2}]},
{"op": "delete",
"table": "ordinals",
"where": [["number", "<", 2]]}]' \
'["advance", 5]' \
'[{"op": "insert",
"table": "ordinals",
"row": {"number": 2, "name": "two"}}]' \
'["advance", 5]' \
'[{"op": "select",
"table": "ordinals",
"where": []}]']],
[[t=0: trigger 0 (immediate): [{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]
t=5: new trigger 1
t=5: new trigger 2
t=10: trigger 3 (immediate): [{"uuid":["uuid","<2>"]}]
t=10: trigger 2 (delayed): [{},{"count":2}]
t=15: trigger 1 (delayed): [{},{"count":1},{"uuid":["uuid","<3>"]}]
t=15: trigger 4 (immediate): [{"rows":[{"_uuid":["uuid","<3>"],"_version":["uuid","<4>"],"name":"three","number":3}]}]
]])

90
tests/ovsdb-types.at Normal file
View File

@@ -0,0 +1,90 @@
AT_BANNER([OVSDB -- atomic types])
OVSDB_CHECK_POSITIVE([integer],
[[parse-atomic-type '["integer"]' ]], ["integer"])
OVSDB_CHECK_POSITIVE([real],
[[parse-atomic-type '["real"]' ]], ["real"])
OVSDB_CHECK_POSITIVE([boolean],
[[parse-atomic-type '["boolean"]' ]], ["boolean"])
OVSDB_CHECK_POSITIVE([string],
[[parse-atomic-type '["string"]' ]], ["string"])
OVSDB_CHECK_POSITIVE([uuid],
[[parse-atomic-type '["uuid"]' ]], ["uuid"])
OVSDB_CHECK_NEGATIVE([void is not a valid atomic-type],
[[parse-atomic-type '["void"]' ]], ["void" is not an atomic-type])
AT_BANNER([OVSDB -- simple types])
OVSDB_CHECK_POSITIVE([simple integer],
[[parse-type '["integer"]' ]], ["integer"])
OVSDB_CHECK_POSITIVE([simple real],
[[parse-type '["real"]' ]], ["real"])
OVSDB_CHECK_POSITIVE([simple boolean],
[[parse-type '["boolean"]' ]], ["boolean"])
OVSDB_CHECK_POSITIVE([simple string],
[[parse-type '["string"]' ]], ["string"])
OVSDB_CHECK_POSITIVE([simple uuid],
[[parse-type '["uuid"]' ]], ["uuid"])
OVSDB_CHECK_POSITIVE([integer in object],
[[parse-type '{"key": "integer"}' ]], ["integer"])
OVSDB_CHECK_POSITIVE([real in object with explicit min and max],
[[parse-type '{"key": "real", "min": 1, "max": 1}' ]], ["real"])
OVSDB_CHECK_NEGATIVE([key type is required],
[[parse-type '{}' ]], [Required 'key' member is missing.])
OVSDB_CHECK_NEGATIVE([void is not a valid type],
[[parse-type '["void"]' ]], ["void" is not an atomic-type])
AT_BANNER([OVSDB -- set types])
OVSDB_CHECK_POSITIVE([optional boolean],
[[parse-type '{"key": "boolean", "min": 0}' ]],
[[{"key":"boolean","min":0}]],
[set])
OVSDB_CHECK_POSITIVE([set of 1 to 3 uuids],
[[parse-type '{"key": "uuid", "min": 1, "max": 3}' ]],
[[{"key":"uuid","max":3}]])
OVSDB_CHECK_POSITIVE([set of 0 to 3 strings],
[[parse-type '{"key": "string", "min": 0, "max": 3}' ]],
[[{"key":"string","max":3,"min":0}]])
OVSDB_CHECK_POSITIVE([set of 0 or more integers],
[[parse-type '{"key": "integer", "min": 0, "max": "unlimited"}']],
[[{"key":"integer","max":"unlimited","min":0}]])
OVSDB_CHECK_POSITIVE([set of 10 or more reals],
[[parse-type '{"key": "real", "min": 10, "max": "unlimited"}']],
[[{"key":"real","max":"unlimited","min":10}]])
OVSDB_CHECK_NEGATIVE([set max cannot be less than min],
[[parse-type '{"key": "real", "min": 5, "max": 3}' ]],
[ovsdb type fails constraint checks])
OVSDB_CHECK_NEGATIVE([set max cannot be negative],
[[parse-type '{"key": "real", "max": -1}' ]],
[bad min or max value])
OVSDB_CHECK_NEGATIVE([set min cannot be negative],
[[parse-type '{"key": "real", "min": -1}' ]],
[bad min or max value])
AT_BANNER([OVSDB -- map types])
OVSDB_CHECK_POSITIVE([map of 1 integer to boolean],
[[parse-type '{"key": "integer", "value": "boolean"}' ]],
[[{"key":"integer","value":"boolean"}]])
OVSDB_CHECK_POSITIVE([map of 1 boolean to integer, explicit min and max],
[[parse-type '{"key": "boolean", "value": "integer", "min": 1, "max": 1}' ]],
[[{"key":"boolean","value":"integer"}]])
OVSDB_CHECK_POSITIVE([map of 2 to 5 uuid to real],
[[parse-type '{"key": "uuid", "value": "real", "min": 2, "max": 5}' ]],
[[{"key":"uuid","max":5,"min":2,"value":"real"}]])
OVSDB_CHECK_POSITIVE([map of 0 to 10 string to uuid],
[[parse-type '{"key": "string", "value": "uuid", "min": 0, "max": 10}' ]],
[[{"key":"string","max":10,"min":0,"value":"uuid"}]])
OVSDB_CHECK_POSITIVE([map of 10 to 20 real to string],
[[parse-type '{"key": "real", "value": "string", "min": 10, "max": 20}' ]],
[[{"key":"real","max":20,"min":10,"value":"string"}]])
OVSDB_CHECK_POSITIVE([map of 20 or more string to real],
[[parse-type '{"key": "string", "value": "real", "min": 20, "max": "unlimited"}' ]],
[[{"key":"string","max":"unlimited","min":20,"value":"real"}]])
OVSDB_CHECK_NEGATIVE([map key type is required],
[[parse-type '{"value": "integer"}' ]],
[Required 'key' member is missing.])

35
tests/ovsdb.at Normal file
View File

@@ -0,0 +1,35 @@
m4_define([OVSDB_CHECK_POSITIVE],
[AT_SETUP([$1])
m4_if([$5], [], [],
[AT_XFAIL_IF([m4_version_prereq([$5], [false], [true])])])
AT_KEYWORDS([ovsdb positive $4])
OVS_CHECK_LCOV([test-ovsdb $2], [0], [$3
], [])
AT_CLEANUP])
m4_define([OVSDB_CHECK_NEGATIVE],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb negative $4])
OVS_CHECK_LCOV([test-ovsdb $2], [1], [], [stderr])
m4_assert(m4_len([$3]))
AT_CHECK(
[if grep -F -e "AS_ESCAPE([$3])" stderr
then
:
else
exit 99
fi],
[0], [ignore], [ignore])
AT_CLEANUP])
m4_include([tests/ovsdb-file.at])
m4_include([tests/ovsdb-types.at])
m4_include([tests/ovsdb-data.at])
m4_include([tests/ovsdb-column.at])
m4_include([tests/ovsdb-table.at])
m4_include([tests/ovsdb-row.at])
m4_include([tests/ovsdb-condition.at])
m4_include([tests/ovsdb-query.at])
m4_include([tests/ovsdb-transaction.at])
m4_include([tests/ovsdb-execution.at])
m4_include([tests/ovsdb-trigger.at])

1229
tests/test-ovsdb.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ limitations under the License.])
AT_TESTED([ovs-vswitchd]) AT_TESTED([ovs-vswitchd])
AT_TESTED([ovs-vsctl]) AT_TESTED([ovs-vsctl])
AT_TESTED([perl])
m4_include([tests/lcov-pre.at]) m4_include([tests/lcov-pre.at])
m4_include([tests/library.at]) m4_include([tests/library.at])
@@ -26,6 +27,7 @@ m4_include([tests/json.at])
m4_include([tests/jsonrpc.at]) m4_include([tests/jsonrpc.at])
m4_include([tests/timeval.at]) m4_include([tests/timeval.at])
m4_include([tests/lockfile.at]) m4_include([tests/lockfile.at])
m4_include([tests/ovsdb.at])
m4_include([tests/stp.at]) m4_include([tests/stp.at])
m4_include([tests/ovs-vsctl.at]) m4_include([tests/ovs-vsctl.at])
m4_include([tests/lcov-post.at]) m4_include([tests/lcov-post.at])

21
tests/uuidfilt.pl Executable file
View File

@@ -0,0 +1,21 @@
#! /usr/bin/perl
use strict;
use warnings;
our %uuids;
our $n_uuids = 0;
sub lookup_uuid {
my ($uuid) = @_;
if (!exists($uuids{$uuid})) {
$uuids{$uuid} = $n_uuids++;
}
return "<$uuids{$uuid}>";
}
my $u = '[0-9a-fA-F]';
my $uuid_re = "${u}{8}-${u}{4}-${u}{4}-${u}{4}-${u}{12}";
while (<>) {
s/($uuid_re)/lookup_uuid($1)/eg;
print $_;
}