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:
@@ -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
|
||||||
|
@@ -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 \
|
||||||
|
@@ -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
756
lib/ovsdb-data.c
Normal 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
128
lib/ovsdb-data.h
Normal 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
221
lib/ovsdb-error.c
Normal 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
53
lib/ovsdb-error.h
Normal 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
167
lib/ovsdb-parser.c
Normal 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
74
lib/ovsdb-parser.h
Normal 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
251
lib/ovsdb-types.c
Normal 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
126
lib/ovsdb-types.h
Normal 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
70
lib/sort.c
Normal 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
26
lib/sort.h
Normal 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 */
|
@@ -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
628
ovsdb/SPECS
Normal 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
44
ovsdb/automake.mk
Normal 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
232
ovsdb/column.c
Normal 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
84
ovsdb/column.h
Normal 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
284
ovsdb/condition.c
Normal 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
72
ovsdb/condition.h
Normal 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
613
ovsdb/execution.c
Normal 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
360
ovsdb/file.c
Normal 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
36
ovsdb/file.h
Normal 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
362
ovsdb/jsonrpc-server.c
Normal 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
29
ovsdb/jsonrpc-server.h
Normal 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
223
ovsdb/ovsdb-server.c
Normal 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
204
ovsdb/ovsdb-tool.c
Normal 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
262
ovsdb/ovsdb.c
Normal 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
73
ovsdb/ovsdb.h
Normal 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
99
ovsdb/query.c
Normal 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
37
ovsdb/query.h
Normal 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
386
ovsdb/row.c
Normal 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
139
ovsdb/row.h
Normal 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
228
ovsdb/table.c
Normal 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
63
ovsdb/table.h
Normal 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
444
ovsdb/transaction.c
Normal 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
41
ovsdb/transaction.h
Normal 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
129
ovsdb/trigger.c
Normal 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
44
ovsdb/trigger.h
Normal 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 */
|
@@ -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
|
||||||
|
@@ -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
18
tests/ovsdb-column.at
Normal 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
558
tests/ovsdb-condition.at
Normal 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
259
tests/ovsdb-data.at
Normal 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
321
tests/ovsdb-execution.at
Normal 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
282
tests/ovsdb-file.at
Normal 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
535
tests/ovsdb-query.at
Normal 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
277
tests/ovsdb-row.at
Normal 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
31
tests/ovsdb-table.at
Normal 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
384
tests/ovsdb-transaction.at
Normal 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
173
tests/ovsdb-trigger.at
Normal 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
90
tests/ovsdb-types.at
Normal 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
35
tests/ovsdb.at
Normal 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
1229
tests/test-ovsdb.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
21
tests/uuidfilt.pl
Executable 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 $_;
|
||||||
|
}
|
Reference in New Issue
Block a user