dist_pkgdata_SCRIPTS =
dist_sbin_SCRIPTS =
man_MANS =
+noinst_DATA =
noinst_HEADERS =
noinst_LIBRARIES =
noinst_PROGRAMS =
noinst_SCRIPTS =
+SUFFIXES =
EXTRA_DIST += soexpand.pl
ro_c = echo '/* -*- mode: c; buffer-read-only: t -*- */'
-SUFFIXES = .in
+SUFFIXES += .in
.in:
$(PERL) $(srcdir)/soexpand.pl -I$(srcdir) < $< | \
sed \
lib/ovsdb-data.h \
lib/ovsdb-error.c \
lib/ovsdb-error.h \
+ lib/ovsdb-idl-provider.h \
+ lib/ovsdb-idl.c \
+ lib/ovsdb-idl.h \
lib/ovsdb-parser.c \
lib/ovsdb-parser.h \
lib/ovsdb-types.c \
--- /dev/null
+/* 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_IDL_PROVIDER_H
+#define OVSDB_IDL_PROVIDER_H 1
+
+#include "hmap.h"
+#include "list.h"
+#include "ovsdb-idl.h"
+#include "ovsdb-types.h"
+#include "shash.h"
+#include "uuid.h"
+
+struct ovsdb_idl_row {
+ struct hmap_node hmap_node; /* In struct ovsdb_idl_table's 'rows'. */
+ struct uuid uuid; /* Row "_uuid" field. */
+ struct list src_arcs; /* Forward arcs (ovsdb_idl_arc.src_node). */
+ struct list dst_arcs; /* Backward arcs (ovsdb_idl_arc.dst_node). */
+ struct ovsdb_idl_table *table; /* Containing table. */
+ struct ovsdb_datum *fields; /* Row data, or null if orphaned. */
+};
+
+struct ovsdb_idl_column {
+ char *name;
+ struct ovsdb_type type;
+};
+
+struct ovsdb_idl_table_class {
+ char *name;
+ const struct ovsdb_idl_column *columns;
+ size_t n_columns;
+ size_t allocation_size;
+ void (*parse)(struct ovsdb_idl_row *);
+ void (*unparse)(struct ovsdb_idl_row *);
+};
+
+struct ovsdb_idl_table {
+ const struct ovsdb_idl_table_class *class;
+ struct shash columns; /* Contains "const struct ovsdb_idl_column *"s. */
+ struct hmap rows; /* Contains "struct ovsdb_idl_row"s. */
+ struct ovsdb_idl *idl; /* Containing idl. */
+};
+
+struct ovsdb_idl_class {
+ const struct ovsdb_idl_table_class *tables;
+ size_t n_tables;
+};
+
+struct ovsdb_idl_row *ovsdb_idl_get_row_arc(
+ struct ovsdb_idl_row *src,
+ struct ovsdb_idl_table_class *dst_table,
+ const struct uuid *dst_uuid);
+
+struct ovsdb_idl_row *ovsdb_idl_first_row(
+ const struct ovsdb_idl *, const struct ovsdb_idl_table_class *);
+
+struct ovsdb_idl_row *ovsdb_idl_next_row(const struct ovsdb_idl_row *);
+
+#endif /* ovsdb-idl-provider.h */
--- /dev/null
+/* 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-idl.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "json.h"
+#include "jsonrpc.h"
+#include "ovsdb-data.h"
+#include "ovsdb-error.h"
+#include "ovsdb-idl-provider.h"
+#include "shash.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_ovsdb_idl
+#include "vlog.h"
+
+/* An arc from one idl_row to another. When row A contains a UUID that
+ * references row B, this is represented by an arc from A (the source) to B
+ * (the destination).
+ *
+ * Arcs from a row to itself are omitted, that is, src and dst are always
+ * different.
+ *
+ * Arcs are never duplicated, that is, even if there are multiple references
+ * from A to B, there is only a single arc from A to B.
+ *
+ * Arcs are directed: an arc from A to B is the converse of an an arc from B to
+ * A. Both an arc and its converse may both be present, if each row refers
+ * to the other circularly.
+ *
+ * The source and destination row may be in the same table or in different
+ * tables.
+ */
+struct ovsdb_idl_arc {
+ struct list src_node; /* In src->src_arcs list. */
+ struct list dst_node; /* In dst->dst_arcs list. */
+ struct ovsdb_idl_row *src; /* Source row. */
+ struct ovsdb_idl_row *dst; /* Destination row. */
+};
+
+struct ovsdb_idl {
+ struct jsonrpc_session *session;
+ struct shash tables;
+ struct json *monitor_request_id;
+ unsigned int last_monitor_request_seqno;
+ unsigned int change_seqno;
+};
+
+static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static void ovsdb_idl_clear(struct ovsdb_idl *);
+static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *);
+static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *);
+static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *,
+ const struct json *);
+static void ovsdb_idl_process_update(struct ovsdb_idl_table *,
+ const struct uuid *,
+ const struct json *old,
+ const struct json *new);
+static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *);
+static void ovsdb_idl_delete_row(struct ovsdb_idl_row *);
+static void ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *);
+
+static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *);
+static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *,
+ const struct uuid *);
+static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *);
+
+static void ovsdb_idl_row_clear_fields(struct ovsdb_idl_row *);
+
+struct ovsdb_idl *
+ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class)
+{
+ struct ovsdb_idl *idl;
+ size_t i;
+
+ idl = xzalloc(sizeof *idl);
+ idl->session = jsonrpc_session_open(remote);
+ shash_init(&idl->tables);
+ for (i = 0; i < class->n_tables; i++) {
+ const struct ovsdb_idl_table_class *tc = &class->tables[i];
+ struct ovsdb_idl_table *table;
+ size_t j;
+
+ table = xmalloc(sizeof *table);
+ assert(!shash_find(&idl->tables, tc->name));
+ shash_add(&idl->tables, tc->name, table);
+ table->class = tc;
+ shash_init(&table->columns);
+ for (j = 0; j < tc->n_columns; j++) {
+ const struct ovsdb_idl_column *column = &tc->columns[j];
+
+ assert(!shash_find(&table->columns, column->name));
+ shash_add(&table->columns, column->name, column);
+ }
+ hmap_init(&table->rows);
+ table->idl = idl;
+ }
+ idl->last_monitor_request_seqno = UINT_MAX;
+
+ return idl;
+}
+
+void
+ovsdb_idl_destroy(struct ovsdb_idl *idl)
+{
+ if (idl) {
+ struct shash_node *node;
+
+ ovsdb_idl_clear(idl);
+ jsonrpc_session_close(idl->session);
+
+ SHASH_FOR_EACH (node, &idl->tables) {
+ struct ovsdb_idl_table *table = node->data;
+
+ shash_destroy(&table->columns);
+ hmap_destroy(&table->rows);
+ }
+ shash_destroy(&idl->tables);
+ json_destroy(idl->monitor_request_id);
+ free(idl);
+ }
+}
+
+static void
+ovsdb_idl_clear(struct ovsdb_idl *idl)
+{
+ struct shash_node *node;
+ bool changed = false;
+
+ SHASH_FOR_EACH (node, &idl->tables) {
+ struct ovsdb_idl_table *table = node->data;
+ struct ovsdb_idl_row *row, *next_row;
+
+ if (hmap_is_empty(&table->rows)) {
+ continue;
+ }
+
+ changed = true;
+ HMAP_FOR_EACH_SAFE (row, next_row, struct ovsdb_idl_row, hmap_node,
+ &table->rows) {
+ struct ovsdb_idl_arc *arc, *next_arc;
+
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ (row->table->class->unparse)(row);
+ ovsdb_idl_row_clear_fields(row);
+ }
+ hmap_remove(&table->rows, &row->hmap_node);
+ LIST_FOR_EACH_SAFE (arc, next_arc, struct ovsdb_idl_arc, src_node,
+ &row->src_arcs) {
+ free(arc);
+ }
+ /* No need to do anything with dst_arcs: some node has those arcs
+ * as forward arcs and will destroy them itself. */
+
+ free(row);
+ }
+ }
+
+ if (changed) {
+ idl->change_seqno++;
+ }
+}
+
+void
+ovsdb_idl_run(struct ovsdb_idl *idl)
+{
+ int i;
+
+ jsonrpc_session_run(idl->session);
+ for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) {
+ struct jsonrpc_msg *msg, *reply;
+ unsigned int seqno;
+
+ seqno = jsonrpc_session_get_seqno(idl->session);
+ if (idl->last_monitor_request_seqno != seqno) {
+ idl->last_monitor_request_seqno = seqno;
+ ovsdb_idl_send_monitor_request(idl);
+ break;
+ }
+
+ msg = jsonrpc_session_recv(idl->session);
+ if (!msg) {
+ break;
+ }
+
+ reply = NULL;
+ if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
+ reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
+ } else if (msg->type == JSONRPC_NOTIFY
+ && !strcmp(msg->method, "update")
+ && msg->params->type == JSON_ARRAY
+ && msg->params->u.array.n == 2
+ && msg->params->u.array.elems[0]->type == JSON_NULL) {
+ ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]);
+ } else if (msg->type == JSONRPC_REPLY
+ && idl->monitor_request_id
+ && json_equal(idl->monitor_request_id, msg->id)) {
+ json_destroy(idl->monitor_request_id);
+ idl->monitor_request_id = NULL;
+ ovsdb_idl_clear(idl);
+ ovsdb_idl_parse_update(idl, msg->result);
+ } else if (msg->type == JSONRPC_REPLY
+ && msg->id && msg->id->type == JSON_STRING
+ && !strcmp(msg->id->u.string, "echo")) {
+ /* It's a reply to our echo request. Ignore it. */
+ } else {
+ VLOG_WARN("%s: received unexpected %s message",
+ jsonrpc_session_get_name(idl->session),
+ jsonrpc_msg_type_to_string(msg->type));
+ jsonrpc_session_force_reconnect(idl->session);
+ }
+ if (reply) {
+ jsonrpc_session_send(idl->session, reply);
+ }
+ jsonrpc_msg_destroy(msg);
+ }
+}
+
+void
+ovsdb_idl_wait(struct ovsdb_idl *idl)
+{
+ jsonrpc_session_wait(idl->session);
+ jsonrpc_session_recv_wait(idl->session);
+}
+
+unsigned int
+ovsdb_idl_get_seqno(const struct ovsdb_idl *idl)
+{
+ return idl->change_seqno;
+}
+
+void
+ovsdb_idl_force_reconnect(struct ovsdb_idl *idl)
+{
+ jsonrpc_session_force_reconnect(idl->session);
+}
+\f
+static void
+ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl)
+{
+ struct json *monitor_requests;
+ const struct shash_node *node;
+ struct jsonrpc_msg *msg;
+
+ monitor_requests = json_object_create();
+ SHASH_FOR_EACH (node, &idl->tables) {
+ const struct ovsdb_idl_table *table = node->data;
+ const struct ovsdb_idl_table_class *tc = table->class;
+ struct json *monitor_request, *columns;
+ size_t i;
+
+ monitor_request = json_object_create();
+ columns = json_array_create_empty();
+ for (i = 0; i < tc->n_columns; i++) {
+ const struct ovsdb_idl_column *column = &tc->columns[i];
+ json_array_add(columns, json_string_create(column->name));
+ }
+ json_object_put(monitor_request, "columns", columns);
+ json_object_put(monitor_requests, tc->name, monitor_request);
+ }
+
+ json_destroy(idl->monitor_request_id);
+ msg = jsonrpc_create_request(
+ "monitor", json_array_create_2(json_null_create(), monitor_requests),
+ &idl->monitor_request_id);
+ jsonrpc_session_send(idl->session, msg);
+}
+
+static void
+ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates)
+{
+ struct ovsdb_error *error;
+
+ idl->change_seqno++;
+
+ error = ovsdb_idl_parse_update__(idl, table_updates);
+ if (error) {
+ if (!VLOG_DROP_WARN(&syntax_rl)) {
+ char *s = ovsdb_error_to_string(error);
+ VLOG_WARN_RL(&syntax_rl, "%s", s);
+ free(s);
+ }
+ ovsdb_error_destroy(error);
+ }
+}
+
+static struct ovsdb_error *
+ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
+ const struct json *table_updates)
+{
+ const struct shash_node *tables_node;
+
+ if (table_updates->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(table_updates, NULL,
+ "<table-updates> is not an object");
+ }
+ SHASH_FOR_EACH (tables_node, json_object(table_updates)) {
+ const struct json *table_update = tables_node->data;
+ const struct shash_node *table_node;
+ struct ovsdb_idl_table *table;
+
+ table = shash_find_data(&idl->tables, tables_node->name);
+ if (!table) {
+ return ovsdb_syntax_error(
+ table_updates, NULL,
+ "<table-updates> includes unknown table \"%s\"",
+ tables_node->name);
+ }
+
+ if (table_update->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(table_update, NULL,
+ "<table-update> for table \"%s\" is "
+ "not an object", table->class->name);
+ }
+ SHASH_FOR_EACH (table_node, json_object(table_update)) {
+ const struct json *row_update = table_node->data;
+ const struct json *old_json, *new_json;
+ struct uuid uuid;
+
+ if (!uuid_from_string(&uuid, table_node->name)) {
+ return ovsdb_syntax_error(table_update, NULL,
+ "<table-update> for table \"%s\" "
+ "contains bad UUID "
+ "\"%s\" as member name",
+ table->class->name,
+ table_node->name);
+ }
+ if (row_update->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(row_update, NULL,
+ "<table-update> for table \"%s\" "
+ "contains <row-update> for %s that "
+ "is not an object",
+ table->class->name,
+ table_node->name);
+ }
+
+ old_json = shash_find_data(json_object(row_update), "old");
+ new_json = shash_find_data(json_object(row_update), "new");
+ if (old_json && old_json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(old_json, NULL,
+ "\"old\" <row> is not object");
+ } else if (new_json && new_json->type != JSON_OBJECT) {
+ return ovsdb_syntax_error(new_json, NULL,
+ "\"new\" <row> is not object");
+ } else if ((old_json != NULL) + (new_json != NULL)
+ != shash_count(json_object(row_update))) {
+ return ovsdb_syntax_error(row_update, NULL,
+ "<row-update> contains unexpected "
+ "member");
+ } else if (!old_json && !new_json) {
+ return ovsdb_syntax_error(row_update, NULL,
+ "<row-update> missing \"old\" "
+ "and \"new\" members");
+ }
+
+ ovsdb_idl_process_update(table, &uuid, old_json, new_json);
+ }
+ }
+
+ return NULL;
+}
+
+static struct ovsdb_idl_row *
+ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid)
+{
+ struct ovsdb_idl_row *row;
+
+ HMAP_FOR_EACH_WITH_HASH (row, struct ovsdb_idl_row, hmap_node,
+ uuid_hash(uuid), &table->rows) {
+ if (uuid_equals(&row->uuid, uuid)) {
+ return row;
+ }
+ }
+ return NULL;
+}
+
+static void
+ovsdb_idl_process_update(struct ovsdb_idl_table *table,
+ const struct uuid *uuid, const struct json *old,
+ const struct json *new)
+{
+ struct ovsdb_idl_row *row;
+
+ row = ovsdb_idl_get_row(table, uuid);
+ if (!new) {
+ /* Delete row. */
+ if (row && !ovsdb_idl_row_is_orphan(row)) {
+ /* XXX perhaps we should check the 'old' values? */
+ ovsdb_idl_delete_row(row);
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" "
+ "from table %s",
+ UUID_ARGS(uuid), table->class->name);
+ }
+ } else if (!old) {
+ /* Insert row. */
+ if (!row) {
+ ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
+ } else if (ovsdb_idl_row_is_orphan(row)) {
+ ovsdb_idl_insert_row(row, new);
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to "
+ "table %s", UUID_ARGS(uuid), table->class->name);
+ ovsdb_idl_modify_row(row, new);
+ }
+ } else {
+ /* Modify row. */
+ if (row) {
+ /* XXX perhaps we should check the 'old' values? */
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ ovsdb_idl_modify_row(row, new);
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot modify missing but "
+ "referenced row "UUID_FMT" in table %s",
+ UUID_ARGS(uuid), table->class->name);
+ ovsdb_idl_insert_row(row, new);
+ }
+ } else {
+ VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" "
+ "in table %s", UUID_ARGS(uuid), table->class->name);
+ ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
+ }
+ }
+}
+
+static void
+ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json)
+{
+ struct ovsdb_idl_table *table = row->table;
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(row_json)) {
+ const char *column_name = node->name;
+ const struct ovsdb_idl_column *column;
+ struct ovsdb_datum datum;
+ struct ovsdb_error *error;
+
+ column = shash_find_data(&table->columns, column_name);
+ if (!column) {
+ VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT,
+ column_name, UUID_ARGS(&row->uuid));
+ continue;
+ }
+
+ error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL);
+ if (!error) {
+ ovsdb_datum_swap(&row->fields[column - table->class->columns],
+ &datum);
+ ovsdb_datum_destroy(&datum, &column->type);
+ } else {
+ char *s = ovsdb_error_to_string(error);
+ VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT
+ " in table %s: %s", column_name,
+ UUID_ARGS(&row->uuid), table->class->name, s);
+ free(s);
+ ovsdb_error_destroy(error);
+ }
+ }
+}
+
+static bool
+ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row)
+{
+ return !row->fields;
+}
+
+static void
+ovsdb_idl_row_clear_fields(struct ovsdb_idl_row *row)
+{
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ const struct ovsdb_idl_table_class *class = row->table->class;
+ size_t i;
+
+ for (i = 0; i < class->n_columns; i++) {
+ ovsdb_datum_destroy(&row->fields[i], &class->columns[i].type);
+ }
+ row->fields = NULL;
+ }
+}
+
+static void
+ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts)
+{
+ struct ovsdb_idl_arc *arc, *next;
+
+ /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows
+ * that this causes to be unreferenced. */
+ LIST_FOR_EACH_SAFE (arc, next, struct ovsdb_idl_arc, src_node,
+ &row->src_arcs) {
+ list_remove(&arc->dst_node);
+ if (destroy_dsts
+ && ovsdb_idl_row_is_orphan(arc->dst)
+ && list_is_empty(&arc->dst->dst_arcs)) {
+ ovsdb_idl_row_destroy(arc->dst);
+ }
+ free(arc);
+ }
+ list_init(&row->src_arcs);
+}
+
+/* Force nodes that reference 'row' to reparse. */
+static void
+ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row, bool destroy_dsts)
+{
+ struct ovsdb_idl_arc *arc, *next;
+
+ /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy
+ * 'arc', so we need to use the "safe" variant of list traversal. However,
+ * calling ref->table->class->parse will add an arc equivalent to 'arc' to
+ * row->arcs. That could be a problem for traversal, but it adds it at the
+ * beginning of the list to prevent us from stumbling upon it again.
+ *
+ * (If duplicate arcs were possible then we would need to make sure that
+ * 'next' didn't also point into 'arc''s destination, but we forbid
+ * duplicate arcs.) */
+ LIST_FOR_EACH_SAFE (arc, next, struct ovsdb_idl_arc, dst_node,
+ &row->dst_arcs) {
+ struct ovsdb_idl_row *ref = arc->src;
+
+ (ref->table->class->unparse)(ref);
+ ovsdb_idl_row_clear_arcs(ref, destroy_dsts);
+ (ref->table->class->parse)(ref);
+ }
+}
+
+static struct ovsdb_idl_row *
+ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid)
+{
+ struct ovsdb_idl_row *row = xmalloc(table->class->allocation_size);
+ hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid));
+ row->uuid = *uuid;
+ list_init(&row->src_arcs);
+ list_init(&row->dst_arcs);
+ row->table = table;
+ row->fields = NULL;
+ return row;
+}
+
+static void
+ovsdb_idl_row_destroy(struct ovsdb_idl_row *row)
+{
+ if (row) {
+ ovsdb_idl_row_clear_fields(row);
+ hmap_remove(&row->table->rows, &row->hmap_node);
+ free(row);
+ }
+}
+
+static void
+ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json)
+{
+ const struct ovsdb_idl_table_class *class = row->table->class;
+ size_t i;
+
+ assert(!row->fields);
+ row->fields = xmalloc(class->n_columns * sizeof *row->fields);
+ for (i = 0; i < class->n_columns; i++) {
+ ovsdb_datum_init_default(&row->fields[i], &class->columns[i].type);
+ }
+ ovsdb_idl_row_update(row, row_json);
+ (class->parse)(row);
+
+ ovsdb_idl_row_reparse_backrefs(row, false);
+}
+
+static void
+ovsdb_idl_delete_row(struct ovsdb_idl_row *row)
+{
+ (row->table->class->unparse)(row);
+ ovsdb_idl_row_clear_arcs(row, true);
+ ovsdb_idl_row_clear_fields(row);
+ if (list_is_empty(&row->dst_arcs)) {
+ ovsdb_idl_row_destroy(row);
+ } else {
+ ovsdb_idl_row_reparse_backrefs(row, true);
+ }
+}
+
+static void
+ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json)
+{
+ (row->table->class->unparse)(row);
+ ovsdb_idl_row_clear_arcs(row, true);
+ ovsdb_idl_row_update(row, row_json);
+ (row->table->class->parse)(row);
+}
+
+static bool
+may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst)
+{
+ const struct ovsdb_idl_arc *arc;
+
+ /* No self-arcs. */
+ if (src == dst) {
+ return false;
+ }
+
+ /* No duplicate arcs.
+ *
+ * We only need to test whether the first arc in dst->dst_arcs originates
+ * at 'src', since we add all of the arcs from a given source in a clump
+ * (in a single call to a row's ->parse function) and new arcs are always
+ * added at the front of the dst_arcs list. */
+ if (list_is_empty(&dst->dst_arcs)) {
+ return true;
+ }
+ arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node);
+ return arc->src != src;
+}
+
+struct ovsdb_idl_row *
+ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src,
+ struct ovsdb_idl_table_class *dst_table_class,
+ const struct uuid *dst_uuid)
+{
+ struct ovsdb_idl *idl = src->table->idl;
+ struct ovsdb_idl_arc *arc;
+ struct ovsdb_idl_row *dst;
+
+ dst = ovsdb_idl_get_row(src->table, dst_uuid);
+ if (!dst) {
+ struct ovsdb_idl_table *dst_table;
+ dst_table = shash_find_data(&idl->tables, dst_table_class->name);
+ dst = ovsdb_idl_row_create(dst_table, dst_uuid);
+ }
+
+ /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */
+ if (may_add_arc(src, dst)) {
+ /* The arc *must* be added at the front of the dst_arcs list. See
+ * ovsdb_idl_row_reparse_backrefs() for details. */
+ arc = xmalloc(sizeof *arc);
+ list_push_front(&src->src_arcs, &arc->src_node);
+ list_push_front(&dst->dst_arcs, &arc->dst_node);
+ arc->src = src;
+ arc->dst = dst;
+ }
+
+ return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL;
+}
+
+static struct ovsdb_idl_row *
+next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node)
+{
+ for (; node; node = hmap_next(&table->rows, node)) {
+ struct ovsdb_idl_row *row;
+
+ row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node);
+ if (!ovsdb_idl_row_is_orphan(row)) {
+ return row;
+ }
+ }
+ return NULL;
+}
+
+struct ovsdb_idl_row *
+ovsdb_idl_first_row(const struct ovsdb_idl *idl,
+ const struct ovsdb_idl_table_class *table_class)
+{
+ struct ovsdb_idl_table *table;
+
+ table = shash_find_data(&idl->tables, table_class->name);
+ return next_real_row(table, hmap_first(&table->rows));
+}
+
+struct ovsdb_idl_row *
+ovsdb_idl_next_row(const struct ovsdb_idl_row *row)
+{
+ struct ovsdb_idl_table *table = row->table;
+
+ return next_real_row(table, hmap_next(&table->rows, &row->hmap_node));
+}
--- /dev/null
+/* 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_IDL_H
+#define OVSDB_IDL_H 1
+
+struct ovsdb_idl_class;
+
+struct ovsdb_idl *ovsdb_idl_create(const char *remote,
+ const struct ovsdb_idl_class *);
+void ovsdb_idl_destroy(struct ovsdb_idl *);
+
+void ovsdb_idl_run(struct ovsdb_idl *);
+void ovsdb_idl_wait(struct ovsdb_idl *);
+
+unsigned int ovsdb_idl_get_seqno(const struct ovsdb_idl *);
+void ovsdb_idl_force_reconnect(struct ovsdb_idl *);
+
+#endif /* ovsdb-idl.h */
ovsdb/ovsdb-idlc.in \
ovsdb/ovsdb-idlc.1
DISTCLEANFILES += ovsdb/ovsdb-idlc
+SUFFIXES += .ovsidl
+.ovsidl.c:
+ $(PYTHON) $(srcdir)/ovsdb/ovsdb-idlc.in c-idl-source $< > $@.tmp
+ mv $@.tmp $@
+.ovsidl.h:
+ $(PYTHON) $(srcdir)/ovsdb/ovsdb-idlc.in c-idl-header $< > $@.tmp
+ mv $@.tmp $@
+.ovsidl.ovsschema:
+ $(PYTHON) $(srcdir)/ovsdb/ovsdb-idlc.in ovsdb-schema $< > $@.tmp
+ mv $@.tmp $@
Lines that begin with \fB//\fR (two forward slashes) are ignored and
thus can be used for comments.
.
+.IP "\fB""\fBidlPrefix\fR"" member of <database-schema>"
+This member, which is required, specifies a string that is prefixed to
+top-level names in C bindings. It should probably end in an
+underscore.
+.
+.IP "\fB""\fBidlHeader\fR"" member of <database-schema>"
+This member, which is required, specifies the name of the IDL header.
+It will be output on an \fB#include\fR line in the source file
+generated by the C bindings. It should include the bracketing
+\fB""\fR or \fB<>\fR.
+.
.IP "\fB""\fBkeyRefTable\fR"" member of <type>"
A <type> whose \fBkey\fR is \fB"uuid"\fR may have an additional member
named \fB"keyRefTable"\fR, whose value is a table name. This
return member
class DbSchema:
- def __init__(self, name, comment, tables):
+ def __init__(self, name, comment, tables, idlPrefix, idlHeader):
self.name = name
self.comment = comment
self.tables = tables
+ self.idlPrefix = idlPrefix
+ self.idlHeader = idlHeader
@staticmethod
def fromJson(json):
comment = getMember(json, 'comment', [unicode], 'database')
tablesJson = mustGetMember(json, 'tables', [dict], 'database')
tables = {}
- for name, json in tablesJson.iteritems():
- tables[name] = TableSchema.fromJson(json, "%s table" % name)
- return DbSchema(name, comment, tables)
+ for name, tableJson in tablesJson.iteritems():
+ tables[name] = TableSchema.fromJson(tableJson, "%s table" % name)
+ idlPrefix = mustGetMember(json, 'idlPrefix', [unicode], 'database')
+ idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database')
+ return DbSchema(name, comment, tables, idlPrefix, idlHeader)
def toJson(self):
d = {"name": self.name,
'string': 'char *'}[type]
def printCIDLHeader(schema):
- prefix = 'ovsrec_'
+ prefix = schema.idlPrefix
print '''\
/* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include "ovsdb-idl-provider.h"
#include "uuid.h"''' % {'prefix': prefix.upper()}
for tableName, table in schema.tables.iteritems():
print
print "/* %s table (%s). */" % (tableName, table.comment)
else:
print "/* %s table. */" % (tableName)
- print "struct %s%s {" % (prefix, tableName.lower())
- print "\t/* Columns automatically included in every table. */"
- print "\tstruct uuid uuid_;"
- print "\tstruct uuid version_;"
+ structName = "%s%s" % (prefix, tableName.lower())
+ print "struct %s {" % structName
+ print "\tstruct ovsdb_idl_row header_;"
for columnName, column in table.columns.iteritems():
print "\n\t/* %s column. */" % columnName
type = column.type
print "\t%s%s%s;" % (cBaseType(prefix, type.key, type.keyRefTable), pointer, columnName)
if not singleton:
print "\tsize_t n_%s;" % columnName
+ print '''
+};
+
+const struct %(s)s *%(s)s_first(const struct ovsdb_idl *);
+const struct %(s)s *%(s)s_next(const struct %(s)s *);
+#define %(S)s_FOR_EACH(ROW, IDL) for ((ROW) = %(s)s_first(IDL); (ROW); (ROW) = %(s)s_next(ROW))''' % {'s': structName, 'S': structName.upper()}
+ print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix
+ print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
+
+def printEnum(members):
+ if len(members) == 0:
+ return
+
+ print "\nenum {";
+ for member in members[:-1]:
+ print " %s," % member
+ print " %s" % members[-1]
+ print "};"
+
+def printCIDLSource(schema):
+ prefix = schema.idlPrefix
+ print '''\
+/* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */
+
+#include <config.h>
+#include %s
+#include <limits.h>
+#include "ovsdb-data.h"''' % schema.idlHeader
+
+ # Table indexes.
+ printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in schema.tables] + ["%sN_TABLES" % prefix.upper()])
+ print "\nstatic struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper())
+
+ for tableName, table in schema.tables.iteritems():
+ structName = "%s%s" % (prefix, tableName.lower())
+ print "\f"
+ if table.comment != None:
+ print "/* %s table (%s). */" % (tableName, table.comment)
+ else:
+ print "/* %s table. */" % (tableName)
+
+ # Column indexes.
+ printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper())
+ for columnName in table.columns]
+ + ["%s_N_COLUMNS" % structName.upper()])
+
+ # Parse function.
+ print '''
+static void
+%s_parse(struct ovsdb_idl_row *row_)
+{
+ struct %s *row = (struct %s *) row_;
+ const struct ovsdb_datum *datum;
+ size_t i UNUSED;
+
+ memset(row_ + 1, 0, sizeof *row - sizeof *row_);''' % (structName, structName, structName)
+
+
+ for columnName, column in table.columns.iteritems():
+ type = column.type
+ refKey = type.key == "uuid" and type.keyRefTable
+ refValue = type.value == "uuid" and type.valueRefTable
+ print
+ print " datum = &row_->fields[%s_COL_%s];" % (structName.upper(), columnName.upper())
+ if type.value:
+ keyVar = "row->key_%s" % columnName
+ valueVar = "row->value_%s" % columnName
+ else:
+ keyVar = "row->%s" % columnName
+ valueVar = None
+
+ if type.min == 1 and type.max == 1:
+ print " if (datum->n >= 1) {"
+ if not refKey:
+ print " %s = datum->keys[0].%s;" % (keyVar, type.key)
+ else:
+ print " %s = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid);" % (keyVar, prefix, type.keyRefTable.lower(), prefix, prefix.upper(), type.keyRefTable.upper())
+
+ if valueVar:
+ if refValue:
+ print " %s = datum->values[0].%s;" % (valueVar, type.value)
+ else:
+ print " %s = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid);" % (valueVar, prefix, type.valueRefTable.lower(), prefix, prefix.upper(), type.valueRefTable.upper())
+ print " }"
+ else:
+ if type.max != 'unlimited':
+ nMax = "MIN(%d, datum->n)" % type.max
+ else:
+ nMax = "datum->n"
+ print " for (i = 0; i < %s; i++) {" % nMax
+ refs = []
+ if refKey:
+ print " struct %s%s *keyRow = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid);" % (prefix, type.keyRefTable.lower(), prefix, type.keyRefTable.lower(), prefix, prefix.upper(), type.keyRefTable.upper())
+ keySrc = "keyRow"
+ refs.append('keyRow')
+ else:
+ keySrc = "datum->keys[i].%s" % type.key
+ if refValue:
+ print " struct %s%s *valueRow = (struct %s%s *) ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid);" % (prefix, type.valueRefTable.lower(), prefix, type.valueRefTable.lower(), prefix, prefix.upper(), type.valueRefTable.upper())
+ valueSrc = "valueRow"
+ refs.append('valueRow')
+ elif valueVar:
+ valueSrc = "datum->values[i].%s" % type.value
+ if refs:
+ print " if (%s) {" % ' && '.join(refs)
+ indent = " "
+ else:
+ indent = " "
+ print "%sif (!row->n_%s) {" % (indent, columnName)
+ print "%s %s = xmalloc(%s * sizeof *%s);" % (indent, keyVar, nMax, keyVar)
+ if valueVar:
+ print "%s %s = xmalloc(%s * sizeof %%s);" % (indent, valueVar, nMax, valueVar)
+ print "%s}" % indent
+ print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc)
+ if valueVar:
+ print "%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc)
+ print "%srow->n_%s++;" % (indent, columnName)
+ if refs:
+ print " }"
+ print " }"
+ print "}"
+
+ # Unparse function.
+ nArrays = 0
+ for columnName, column in table.columns.iteritems():
+ type = column.type
+ if type.min != 1 or type.max != 1:
+ if not nArrays:
+ print '''
+static void
+%s_unparse(struct ovsdb_idl_row *row_)
+{
+ struct %s *row = (struct %s *) row_;
+''' % (structName, structName, structName)
+ if type.value:
+ keyVar = "row->key_%s" % columnName
+ valueVar = "row->value_%s" % columnName
+ else:
+ keyVar = "row->%s" % columnName
+ valueVar = None
+ print " free(%s);" % keyVar
+ if valueVar:
+ print " free(%s);" % valueVar
+ nArrays += 1
+ if not nArrays:
+ print '''
+static void
+%s_unparse(struct ovsdb_idl_row *row UNUSED)
+{''' % (structName)
+ print "}"
+
+ # First, next functions.
+ print '''
+const struct %(s)s *%(s)s_first(const struct ovsdb_idl *idl)
+{
+ return (const struct %(s)s *) ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s]);
+}
+
+const struct %(s)s *%(s)s_next(const struct %(s)s *row)
+{
+ return (const struct %(s)s *) ovsdb_idl_next_row(&row->header_);
+}''' % {'s': structName, 'p': prefix, 'P': prefix.upper(), 'T': tableName.upper()}
+
+ # Table columns.
+ print "\nstatic struct ovsdb_idl_column %s_columns[%s_N_COLUMNS] = {" % (
+ structName, structName.upper())
+ for columnName, column in table.columns.iteritems():
+ type = column.type
+
+ if type.value:
+ valueTypeName = type.value.upper()
+ else:
+ valueTypeName = "VOID"
+ if type.max == "unlimited":
+ max = "UINT_MAX"
+ else:
+ max = type.max
+ print " {\"%s\", {OVSDB_TYPE_%s, OVSDB_TYPE_%s, %d, %s}}," % (
+ columnName, type.key.upper(), valueTypeName,
+ type.min, max)
print "};"
- print
- print "#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
+
+ # Table classes.
+ print "\f"
+ print "static struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
+ for tableName, table in schema.tables.iteritems():
+ structName = "%s%s" % (prefix, tableName.lower())
+ print " {\"%s\"," % tableName
+ print " %s_columns, ARRAY_SIZE(%s_columns)," % (
+ structName, structName)
+ print " sizeof(struct %s)," % structName
+ print " %s_parse," % structName
+ print " %s_unparse}," % structName
+ print "};"
+
+ # IDL class.
+ print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix
+ print " %stable_classes, ARRAY_SIZE(%stable_classes)" % (prefix, prefix)
+ print "};"
def ovsdb_escape(string):
def escape(match):
tests/ovsdb-file.at \
tests/ovsdb-server.at \
tests/ovsdb-monitor.at \
+ tests/ovsdb-idl.at \
tests/stp.at \
tests/ovs-vsctl.at \
tests/lcov-post.at
tests_test_lockfile_LDADD = lib/libopenvswitch.a
noinst_PROGRAMS += tests/test-ovsdb
-tests_test_ovsdb_SOURCES = tests/test-ovsdb.c
+tests_test_ovsdb_SOURCES = tests/test-ovsdb.c tests/idltest.c tests/idltest.h
tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a
-EXTRA_DIST += tests/uuidfilt.pl
+EXTRA_DIST += tests/uuidfilt.pl tests/idltest.ovsidl
+BUILT_SOURCES += tests/idltest.c tests/idltest.h
+noinst_DATA += tests/idltest.ovsschema
+DISTCLEANFILES += tests/idltest.ovsschema
noinst_PROGRAMS += tests/test-reconnect
tests_test_reconnect_SOURCES = tests/test-reconnect.c
--- /dev/null
+//
+// This is an ovsdb-idl schema. The OVSDB IDL compiler, ovsdb-idlc,
+// can translate it into an OVSDB schema (which simply entails
+// deleting some members from the schema) or C headers or source for
+// use with the IDL at runtime.
+//
+
+{"name": "idltest",
+ "idlPrefix": "idltest_",
+ "idlHeader": "\"tests/idltest.h\"",
+ "tables": {
+ "simple": {
+ "columns": {
+ "i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"},
+ "ia": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
+ "ra": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
+ "ba": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
+ "sa": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
+ "ua": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}},
+ "selfLink": {
+ "columns": {
+ "i": {"type": "integer"},
+ "k": {"type": {"key": "uuid", "keyRefTable": "selfLink"}},
+ "ka": {"type": {"key": "uuid", "keyRefTable": "selfLink",
+ "min": 0, "max": "unlimited"}}}}}}
--- /dev/null
+AT_BANNER([OVSDB -- interface description language (IDL)])
+
+# OVSDB_CHECK_IDL(TITLE, [PRE-IDL-TXN], TRANSACTIONS, OUTPUT, [KEYWORDS])
+#
+# Creates a database with a schema derived from idltest.ovsidl, runs
+# each PRE-IDL-TXN (if any), starts an ovsdb-server on that database,
+# and runs "test-ovsdb idl" passing each of the TRANSACTIONS along.
+#
+# Checks that the overall output is OUTPUT. Before comparison, the
+# output is sorted (using "sort") and 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.
+#
+# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
+m4_define([OVSDB_CHECK_IDL],
+ [AT_SETUP([$1])
+ AT_KEYWORDS([ovsdb server idl positive $5])
+ OVS_CHECK_LCOV([ovsdb-tool create db $abs_builddir/idltest.ovsschema],
+ [0], [stdout], [ignore])
+ AT_CHECK([ovsdb-server --detach --pidfile=$PWD/server-pid --listen=punix:socket --unixctl=$PWD/unixctl db])
+ m4_if([$2], [], [],
+ [OVS_CHECK_LCOV([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat server-pid`])])
+ AT_CHECK([test-ovsdb -vjsonrpc -t10 idl unix:socket $3],
+ [0], [stdout], [ignore], [kill `cat server-pid`])
+ AT_CHECK([sort stdout | perl $srcdir/uuidfilt.pl], [0], [$4], [],
+ [kill `cat server-pid`])
+ kill `cat server-pid`
+ AT_CLEANUP])
+
+OVSDB_CHECK_IDL([simple idl, initially empty, no ops],
+ [],
+ [],
+ [000: empty
+001: done
+])
+
+OVSDB_CHECK_IDL([simple idl, initially empty, various ops],
+ [],
+ [['[{"op": "insert",
+ "table": "simple",
+ "row": {"i": 1,
+ "r": 2.0,
+ "b": true,
+ "s": "mystring",
+ "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"],
+ "ia": ["set", [1, 2, 3]],
+ "ra": ["set", [-0.5]],
+ "ba": ["set", [true, false]],
+ "sa": ["set", ["abc", "def"]],
+ "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"],
+ ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}},
+ {"op": "insert",
+ "table": "simple",
+ "row": {}}]' \
+ '[{"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"b": true}}]' \
+ '[{"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"r": 123.5}}]' \
+ '[{"op": "insert",
+ "table": "simple",
+ "row": {"i": -1,
+ "r": 125,
+ "b": false,
+ "s": "",
+ "ia": ["set", [1]],
+ "ra": ["set", [1.5]],
+ "ba": ["set", [false]],
+ "sa": ["set", []],
+ "ua": ["set", []]}}]' \
+ '[{"op": "update",
+ "table": "simple",
+ "where": [["i", "<", 1]],
+ "row": {"s": "newstring"}}]' \
+ '[{"op": "delete",
+ "table": "simple",
+ "where": [["i", "==", 0]]}]' \
+ 'reconnect']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]}]}
+002: i=0 r=0 b=false s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+002: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+003: {"error":null,"result":[{"count":2}]}
+004: i=0 r=0 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+004: i=1 r=2 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+005: {"error":null,"result":[{"count":2}]}
+006: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+006: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+007: {"error":null,"result":[{"uuid":["uuid","<6>"]}]}
+008: i=-1 r=125 b=false s= u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+008: i=0 r=123.5 b=true s= u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+008: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+009: {"error":null,"result":[{"count":2}]}
+010: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+010: i=0 r=123.5 b=true s=newstring u=<2> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+010: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+011: {"error":null,"result":[{"count":1}]}
+012: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+012: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+013: reconnect
+014: i=-1 r=125 b=false s=newstring u=<2> ia=[1] ra=[1.5] ba=[false] sa=[] ua=[] uuid=<6>
+014: i=1 r=123.5 b=true s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<4> <5>] uuid=<0>
+015: done
+]])
+
+OVSDB_CHECK_IDL([simple idl, initially populated],
+ [['[{"op": "insert",
+ "table": "simple",
+ "row": {"i": 1,
+ "r": 2.0,
+ "b": true,
+ "s": "mystring",
+ "u": ["uuid", "84f5c8f5-ac76-4dbc-a24f-8860eb407fc1"],
+ "ia": ["set", [1, 2, 3]],
+ "ra": ["set", [-0.5]],
+ "ba": ["set", [true, false]],
+ "sa": ["set", ["abc", "def"]],
+ "ua": ["set", [["uuid", "69443985-7806-45e2-b35f-574a04e720f9"],
+ ["uuid", "aad11ef0-816a-4b01-93e6-03b8b4256b98"]]]}},
+ {"op": "insert",
+ "table": "simple",
+ "row": {}}]']],
+ [['[{"op": "update",
+ "table": "simple",
+ "where": [],
+ "row": {"b": true}}]']],
+ [[000: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+000: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<3> <4>] uuid=<5>
+001: {"error":null,"result":[{"count":2}]}
+002: i=0 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
+002: i=1 r=2 b=true s=mystring u=<2> ia=[1 2 3] ra=[-0.5] ba=[false true] sa=[abc def] ua=[<3> <4>] uuid=<5>
+003: done
+]])
+
+OVSDB_CHECK_IDL([self-linking idl, consistent ops],
+ [],
+ [['[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 0, "k": ["named-uuid", "self"]},
+ "uuid-name": "self"}]' \
+ '[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 1},
+ "uuid-name": "row1"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 2, "k": ["named-uuid", "row1"]},
+ "uuid-name": "row2"},
+ {"op": "update",
+ "table": "selfLink",
+ "where": [["i", "==", 1]],
+ "row": {"k": ["named-uuid", "row2"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [["i", "==", 1]],
+ "row": {"k": ["uuid", "#1#"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "#0#"]}}]']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]}
+002: i=0 k=0 ka=[] uuid=<0>
+003: {"error":null,"result":[{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"count":1}]}
+004: i=0 k=0 ka=[] uuid=<0>
+004: i=1 k=2 ka=[] uuid=<1>
+004: i=2 k=1 ka=[] uuid=<2>
+005: {"error":null,"result":[{"count":1}]}
+006: i=0 k=0 ka=[] uuid=<0>
+006: i=1 k=1 ka=[] uuid=<1>
+006: i=2 k=1 ka=[] uuid=<2>
+007: {"error":null,"result":[{"count":3}]}
+008: i=0 k=0 ka=[] uuid=<0>
+008: i=1 k=0 ka=[] uuid=<1>
+008: i=2 k=0 ka=[] uuid=<2>
+009: done
+]])
+
+OVSDB_CHECK_IDL([self-linking idl, inconsistent ops],
+ [],
+ [['[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 0, "k": ["uuid", "cf197cc5-c8c9-42f5-82d5-c71a9f2cb96b"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "#0#"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "c2fca39a-e69a-42a4-9c56-5eca85839ce9"]}}]' \
+ '[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 1, "k": ["uuid", "52d752a3-b062-4668-9446-d2e0d4a14703"]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"k": ["uuid", "#1#"]}}]' \
+]],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]}]}
+002: i=0 k= ka=[] uuid=<0>
+003: {"error":null,"result":[{"count":1}]}
+004: i=0 k=0 ka=[] uuid=<0>
+005: {"error":null,"result":[{"count":1}]}
+006: i=0 k= ka=[] uuid=<0>
+007: {"error":null,"result":[{"uuid":["uuid","<1>"]}]}
+008: i=0 k= ka=[] uuid=<0>
+008: i=1 k= ka=[] uuid=<1>
+009: {"error":null,"result":[{"count":2}]}
+010: i=0 k=1 ka=[] uuid=<0>
+010: i=1 k=1 ka=[] uuid=<1>
+011: done
+]])
+
+OVSDB_CHECK_IDL([self-linking idl, sets],
+ [],
+ [['[{"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 0, "ka": ["set", [["named-uuid", "i0"]]]},
+ "uuid-name": "i0"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 1, "ka": ["set", [["named-uuid", "i1"]]]},
+ "uuid-name": "i1"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 2, "ka": ["set", [["named-uuid", "i2"]]]},
+ "uuid-name": "i2"},
+ {"op": "insert",
+ "table": "selfLink",
+ "row": {"i": 3, "ka": ["set", [["named-uuid", "i3"]]]},
+ "uuid-name": "i3"}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "#1#"], ["uuid", "#2#"], ["uuid", "#3#"]]]}}]' \
+ '[{"op": "update",
+ "table": "selfLink",
+ "where": [],
+ "row": {"ka": ["set", [["uuid", "#0#"], ["uuid", "88702e78-845b-4a6e-ad08-cf68922ae84a"], ["uuid", "#2#"], ["uuid", "1ac2b12e-b767-4805-a55d-43976e40c465"]]]}}]']],
+ [[000: empty
+001: {"error":null,"result":[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]}]}
+002: i=0 k= ka=[0] uuid=<0>
+002: i=1 k= ka=[1] uuid=<1>
+002: i=2 k= ka=[2] uuid=<2>
+002: i=3 k= ka=[3] uuid=<3>
+003: {"error":null,"result":[{"count":4}]}
+004: i=0 k= ka=[0 1 2 3] uuid=<0>
+004: i=1 k= ka=[0 1 2 3] uuid=<1>
+004: i=2 k= ka=[0 1 2 3] uuid=<2>
+004: i=3 k= ka=[0 1 2 3] uuid=<3>
+005: {"error":null,"result":[{"count":4}]}
+006: i=0 k= ka=[0 2] uuid=<0>
+006: i=1 k= ka=[0 2] uuid=<1>
+006: i=2 k= ka=[0 2] uuid=<2>
+006: i=3 k= ka=[0 2] uuid=<3>
+007: done
+]])
+
+# XXX self-linking idl, maps
m4_include([tests/ovsdb-file.at])
m4_include([tests/ovsdb-server.at])
m4_include([tests/ovsdb-monitor.at])
+m4_include([tests/ovsdb-idl.at])
#include "command-line.h"
#include "json.h"
+#include "jsonrpc.h"
#include "ovsdb-data.h"
#include "ovsdb-error.h"
+#include "ovsdb-idl.h"
#include "ovsdb-types.h"
#include "ovsdb/column.h"
#include "ovsdb/condition.h"
#include "ovsdb/transaction.h"
#include "ovsdb/trigger.h"
#include "poll-loop.h"
+#include "stream.h"
#include "svec.h"
+#include "tests/idltest.h"
#include "timeval.h"
#include "util.h"
#include "vlog.h"
parse_options(int argc, char *argv[])
{
static struct option long_options[] = {
+ {"timeout", required_argument, 0, 't'},
{"verbose", optional_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
char *short_options = long_options_to_short_options(long_options);
for (;;) {
- int c = getopt_long(argc, argv, short_options, long_options, NULL);
+ unsigned long int timeout;
+ int c;
+
+ c = getopt_long(argc, argv, short_options, long_options, NULL);
if (c == -1) {
break;
}
switch (c) {
+ case 't':
+ timeout = strtoul(optarg, NULL, 10);
+ if (timeout <= 0) {
+ ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
+ optarg);
+ } else {
+ time_alarm(timeout);
+ }
+ break;
+
case 'h':
usage();
" executes each TRANSACTION on an initially empty database\n"
" the specified SCHEMA. A TRANSACTION of the form\n"
" [\"advance\", NUMBER] advances NUMBER milliseconds in\n"
- " simulated time, for causing triggers to time out.\n",
+ " simulated time, for causing triggers to time out.\n"
+ " idl SERVER [TRANSACTION...]\n"
+ " connect to SERVER and dump the contents of the database\n"
+ " as seen initially by the IDL implementation and after\n"
+ " executing each TRANSACTION. (Each TRANSACTION must modify\n"
+ " the database or this command will hang.)\n",
program_name, program_name);
vlog_usage();
printf("\nOther options:\n"
+ " -t, --timeout=SECS give up after SECS seconds\n"
" -h, --help display this help message\n");
exit(EXIT_SUCCESS);
}
ovsdb_destroy(do_transact_db); /* Also destroys 'schema'. */
}
+static int
+compare_selflink(const void *a_, const void *b_)
+{
+ const struct idltest_selflink *const *ap = a_;
+ const struct idltest_selflink *const *bp = b_;
+ const struct idltest_selflink *a = *ap;
+ const struct idltest_selflink *b = *bp;
+
+
+ return a->i < b->i ? -1 : a->i > b->i;
+}
+
+static void
+print_idl(struct ovsdb_idl *idl, int step)
+{
+ const struct idltest_simple *s;
+ const struct idltest_selflink *sl;
+ int n = 0;
+
+ IDLTEST_SIMPLE_FOR_EACH (s, idl) {
+ size_t i;
+
+ printf("%03d: i=%"PRId64" r=%g b=%s s=%s u="UUID_FMT" ia=[",
+ step, s->i, s->r, s->b ? "true" : "false",
+ s->s, UUID_ARGS(&s->u));
+ for (i = 0; i < s->n_ia; i++) {
+ printf("%s%"PRId64, i ? " " : "", s->ia[i]);
+ }
+ printf("] ra=[");
+ for (i = 0; i < s->n_ra; i++) {
+ printf("%s%g", i ? " " : "", s->ra[i]);
+ }
+ printf("] ba=[");
+ for (i = 0; i < s->n_ba; i++) {
+ printf("%s%s", i ? " " : "", s->ba[i] ? "true" : "false");
+ }
+ printf("] sa=[");
+ for (i = 0; i < s->n_sa; i++) {
+ printf("%s%s", i ? " " : "", s->sa[i]);
+ }
+ printf("] ua=[");
+ for (i = 0; i < s->n_ua; i++) {
+ printf("%s"UUID_FMT, i ? " " : "", UUID_ARGS(&s->ua[i]));
+ }
+ printf("] uuid="UUID_FMT"\n", UUID_ARGS(&s->header_.uuid));
+ n++;
+ }
+ IDLTEST_SELFLINK_FOR_EACH (sl, idl) {
+ struct idltest_selflink **links;
+ size_t i;
+
+ printf("%03d: i=%"PRId64" k=", step, sl->i);
+ if (sl->k) {
+ printf("%"PRId64, sl->k->i);
+ }
+ printf(" ka=[");
+ links = xmemdup(sl->ka, sl->n_ka * sizeof *sl->ka);
+ qsort(links, sl->n_ka, sizeof *links, compare_selflink);
+ for (i = 0; i < sl->n_ka; i++) {
+ printf("%s%"PRId64, i ? " " : "", links[i]->i);
+ }
+ free(links);
+ printf("] uuid="UUID_FMT"\n", UUID_ARGS(&sl->header_.uuid));
+ n++;
+ }
+ if (!n) {
+ printf("%03d: empty\n", step);
+ }
+}
+
+static unsigned int
+print_updated_idl(struct ovsdb_idl *idl, struct jsonrpc *rpc,
+ int step, unsigned int seqno)
+{
+ for (;;) {
+ unsigned int new_seqno;
+
+ if (rpc) {
+ jsonrpc_run(rpc);
+ }
+ ovsdb_idl_run(idl);
+ new_seqno = ovsdb_idl_get_seqno(idl);
+ if (new_seqno != seqno) {
+ print_idl(idl, step);
+ return new_seqno;
+ }
+
+ if (rpc) {
+ jsonrpc_wait(rpc);
+ }
+ ovsdb_idl_wait(idl);
+ poll_block();
+ }
+}
+
+static void
+parse_uuids(const struct json *json, struct ovsdb_symbol_table *symtab,
+ size_t *n)
+{
+ struct uuid uuid;
+
+ if (json->type == JSON_STRING && uuid_from_string(&uuid, json->u.string)) {
+ char *name = xasprintf("#%d#", *n);
+ ovsdb_symbol_table_put(symtab, name, &uuid);
+ free(name);
+ *n += 1;
+ } else if (json->type == JSON_ARRAY) {
+ size_t i;
+
+ for (i = 0; i < json->u.array.n; i++) {
+ parse_uuids(json->u.array.elems[i], symtab, n);
+ }
+ } else if (json->type == JSON_OBJECT) {
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(json)) {
+ parse_uuids(node->data, symtab, n);
+ }
+ }
+}
+
+static void
+substitute_uuids(struct json *json, const struct ovsdb_symbol_table *symtab)
+{
+ if (json->type == JSON_STRING) {
+ const struct uuid *uuid;
+
+ uuid = ovsdb_symbol_table_get(symtab, json->u.string);
+ if (uuid) {
+ free(json->u.string);
+ json->u.string = xasprintf(UUID_FMT, UUID_ARGS(uuid));
+ }
+ } else if (json->type == JSON_ARRAY) {
+ size_t i;
+
+ for (i = 0; i < json->u.array.n; i++) {
+ substitute_uuids(json->u.array.elems[i], symtab);
+ }
+ } else if (json->type == JSON_OBJECT) {
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(json)) {
+ substitute_uuids(node->data, symtab);
+ }
+ }
+}
+
+static void
+do_idl(int argc, char *argv[])
+{
+ struct jsonrpc *rpc;
+ struct ovsdb_idl *idl;
+ unsigned int seqno = 0;
+ struct ovsdb_symbol_table *symtab;
+ size_t n_uuids = 0;
+ int step = 0;
+ int error;
+ int i;
+
+ idl = ovsdb_idl_create(argv[1], &idltest_idl_class);
+ if (argc > 2) {
+ struct stream *stream;
+
+ error = stream_open_block(argv[1], &stream);
+ if (error) {
+ ovs_fatal(error, "failed to connect to \"%s\"", argv[1]);
+ }
+ rpc = jsonrpc_open(stream);
+ } else {
+ rpc = NULL;
+ }
+
+ symtab = ovsdb_symbol_table_create();
+ for (i = 2; i < argc; i++) {
+ struct jsonrpc_msg *request, *reply;
+ int error;
+
+ seqno = print_updated_idl(idl, rpc, step++, seqno);
+
+ if (!strcmp(argv[i], "reconnect")) {
+ printf("%03d: reconnect\n", step++);
+ ovsdb_idl_force_reconnect(idl);
+ } else {
+ struct json *json = parse_json(argv[i]);
+ substitute_uuids(json, symtab);
+ request = jsonrpc_create_request("transact", json, NULL);
+ error = jsonrpc_transact_block(rpc, request, &reply);
+ if (error) {
+ ovs_fatal(error, "jsonrpc transaction failed");
+ }
+ printf("%03d: ", step++);
+ if (reply->result) {
+ parse_uuids(reply->result, symtab, &n_uuids);
+ }
+ json_destroy(reply->id);
+ reply->id = NULL;
+ print_and_free_json(jsonrpc_msg_to_json(reply));
+ }
+ }
+ ovsdb_symbol_table_destroy(symtab);
+
+ if (rpc) {
+ jsonrpc_close(rpc);
+ }
+ print_updated_idl(idl, NULL, step++, seqno);
+ ovsdb_idl_destroy(idl);
+ printf("%03d: done\n", step);
+}
+
static struct command all_commands[] = {
{ "log-io", 2, INT_MAX, do_log_io },
{ "parse-atomic-type", 1, 1, do_parse_atomic_type },
{ "transact", 1, INT_MAX, do_transact },
{ "execute", 2, INT_MAX, do_execute },
{ "trigger", 2, INT_MAX, do_trigger },
+ { "idl", 1, INT_MAX, do_idl },
{ "help", 0, INT_MAX, do_help },
{ NULL, 0, 0, NULL },
};
vswitchd/proc-net-compat.h \
vswitchd/ovs-vswitchd.c \
vswitchd/ovs-vswitchd.h \
+ vswitchd/vswitch-idl.c \
vswitchd/vswitch-idl.h \
vswitchd/xenserver.c \
vswitchd/xenserver.h
vswitchd/ovs-vswitchd.8.in \
vswitchd/ovs-brcompatd.8.in
-EXTRA_DIST += vswitchd/vswitch.ovsidl
-BUILT_SOURCES += vswitchd/vswitch-idl.h
-DISTCLEANFILES += \
- vswitchd/vswitch.ovsidl \
- vswitchd/vswitch-idl.h
-vswitchd/vswitch-idl.h: vswitchd/vswitch.ovsidl ovsdb/ovsdb-idlc
- ovsdb/ovsdb-idlc c-idl-header $(srcdir)/vswitchd/vswitch.ovsidl > $@
+EXTRA_DIST += vswitchd/vswitch-idl.ovsidl
+BUILT_SOURCES += vswitchd/vswitch-idl.c vswitchd/vswitch-idl.h
+DISTCLEANFILES += vswitchd/vswitch-idl.c vswitchd/vswitch-idl.h
+noinst_DATA += vswitchd/vswitch-idl.ovsschema
+DISTCLEANFILES += vswitchd/vswitch-idl.ovsschema
--- /dev/null
+//
+// This is an ovsdb-idl schema. The OVSDB IDL compiler, ovsdb-idlc,
+// can translate it into an OVSDB schema (which simply entails
+// deleting some members from the schema) or C headers or source for
+// use with the IDL at runtime.
+//
+
+{"name": "ovs_vswitchd_db",
+ "comment": "Configuration for one Open vSwitch daemon.",
+ "idlPrefix": "ovsrec_",
+ "idlHeader": "\"vswitchd/vswitch-idl.h\"",
+ "tables": {
+ "Open_vSwitch": {
+ "comment": "Configuration for an Open vSwitch daemon.",
+ "columns": {
+ "bridges": {
+ "comment": "Set of bridges managed by the daemon.",
+ "type": {"key": "uuid", "keyRefTable": "Bridge",
+ "min": 0, "max": "unlimited"}},
+ "management_id": {
+ "comment": "Exactly 12 hex digits that identify the daemon.",
+ "type": "string"},
+ "controller": {
+ "comment": "Default Controller used by bridges.",
+ "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}},
+ "ssl": {
+ "comment": "SSL used globally by the daemon.",
+ "type": {"key": "uuid", "keyRefTable": "SSL", "min": 0, "max": 1}}}},
+ "Bridge": {
+ "comment": "Configuration for a bridge within an Open_vSwitch.",
+ "columns": {
+ "name": {
+ "comment": "Bridge identifier. Should be alphanumeric and no more than about 8 bytes long. Must be unique among the names of ports, interfaces, and bridges on a host.",
+ "type": "string"},
+ "datapath_id": {
+ "comment": "OpenFlow datapath ID. Exactly 12 hex digits.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "hwaddr": {
+ "comment": "Ethernet address to use for bridge. Exactly 12 hex digits in the form XX:XX:XX:XX:XX:XX.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "ports": {
+ "comment": "Ports included in the bridge.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
+ "mirrors": {
+ "comment": "Port mirroring configuration.",
+ "type": {"key": "uuid", "keyRefTable": "Mirror", "min": 0, "max": "unlimited"}},
+ "netflow": {
+ "comment": "NetFlow configuration.",
+ "type": {"key": "uuid", "keyRefTable": "NetFlow", "min": 0, "max": "unlimited"}},
+ "controller": {
+ "comment": "OpenFlow controller. If unset, defaults to that specified by the parent Open_vSwitch.",
+ "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}}}},
+ "Port": {
+ "comment": "A port within a Bridge. May contain a single Interface or multiple (bonded) Interfaces.",
+ "columns": {
+ "name": {
+ "comment": "Port name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the interface name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host.",
+ "type": "string"},
+ "interfaces": {
+ "comment": "The Port's Interfaces. If there is more than one, this is a bonded Port.",
+ "type": {"key": "uuid", "keyRefTable": "Interface", "min": 1, "max": "unlimited"}},
+ "trunks": {
+ "comment": "The 802.1Q VLAN(s) that this port trunks. Should be empty if this port trunks all VLAN(s) or if this is not a trunk port.",
+ "type": {"key": "integer", "min": 0, "max": 4096}},
+ "tag": {
+ "comment": "This port's implicitly tagged VLAN. Should be empty if this is a trunk port.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "updelay": {
+ "comment": "For a bonded port, the number of milliseconds for which carrier must stay up on an interface before the interface is considered to be up. Ignored for non-bonded ports.",
+ "type": "integer"},
+ "downdelay": {
+ "comment": "For a bonded port, the number of milliseconds for which carrier must stay down on an interface before the interface is considered to be down. Ignored for non-bonded ports.",
+ "type": "integer"}}},
+ "Interface": {
+ "comment": "An interface within a Port.",
+ "columns": {
+ "name": {
+ "comment": "Interface name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the port name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host.",
+ "type": "string"},
+ "internal": {
+ "comment": "An \"internal\" port is one that is implemented in software as a logical device.",
+ "type": "boolean"},
+ "ingress_policing_rate": {
+ "comment": "Maximum rate for data received on this interface, in kbps. Set to 0 to disable policing.",
+ "type": "integer"},
+ "ingress_policing_burst": {
+ "comment": "Maximum burst size for data received on this interface, in kb. The default burst size if set to 0 is 10 kb.",
+ "type": "integer"}}},
+ "Mirror": {
+ "comment": "A port mirror within a Bridge.",
+ "columns": {
+ "name": {
+ "comment": "Arbitrary identifier for the Mirror.",
+ "type": "string"},
+ "select_src_port": {
+ "comment": "Ports on which arriving packets are selected for mirroring.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
+ "select_dst_port": {
+ "comment": "Ports on which departing packets are selected for mirroring.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
+ "select_vlan": {
+ "comment": "VLANs on which packets are selected for mirroring.",
+ "type": {"key": "integer", "min": 0, "max": 4096}},
+ "output_port": {
+ "comment": "Output port for selected packets. Mutually exclusive with output_vlan.",
+ "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": 1}},
+ "output_vlan": {
+ "comment": "Output VLAN for selected packets. Mutually exclusive with output_port.",
+ "type": {"key": "integer", "min": 0, "max": 1}}}},
+ "NetFlow": {
+ "comment": "A NetFlow target.",
+ "columns": {
+ "target": {
+ "comment": "NetFlow target in the form \"IP:PORT\".",
+ "type": "string"},
+ "engine_type": {
+ "comment": "Engine type to use in NetFlow messages. Defaults to datapath index if not specified.",
+ "type": "integer", "min":0, "max":1},
+ "engine_id": {
+ "comment": "Engine ID to use in NetFlow messages. Defaults to datapath index if not specified.",
+ "type": "integer", "min":0, "max":1},
+ "add_id_to_interface": {
+ "comment": "Place least-significant 7 bits of engine ID into most significant bits of ingress and egress interface fields of NetFlow records?",
+ "type": "boolean"}}},
+ "Controller": {
+ "comment": "An OpenFlow controller.",
+ "columns": {
+ "target": {
+ "comment": "Connection method for controller, e.g. \"ssl:...\", \"tcp:...\". The special string \"discover\" enables controller discovery.",
+ "type": "string"},
+ "max_backoff": {
+ "comment": "Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "inactivity_probe": {
+ "comment": "Maximum number of milliseconds of idle time on connection to controller before sending an inactivity probe message. Default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "fail_mode": {
+ "comment": "Either \"standalone\" or \"secure\", or empty to use the implementation's default.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "discover_accept_regex": {
+ "comment": "If \"target\" is \"discover\", a POSIX extended regular expression against which the discovered controller location is validated. If not specified, the default is implementation-specific.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "discover_update_resolv_conf": {
+ "comment": "If \"target\" is \"discover\", whether to update /etc/resolv.conf when the controller is discovered. If not specified, the default is implementation-specific.",
+ "type": {"key": "boolean", "min": 0, "max": 1}},
+ "connection_mode": {
+ "comment": "Either \"in-band\" or \"out-of-band\". If not specified, the default is implementation-specific.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "local_ip": {
+ "comment": "If \"target\" is not \"discover\", the IP address to configure on the local port.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "local_netmask": {
+ "comment": "If \"target\" is not \"discover\", the IP netmask to configure on the local port.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "local_gateway": {
+ "comment": "If \"target\" is not \"discover\", the IP gateway to configure on the local port.",
+ "type": {"key": "string", "min": 0, "max": 1}},
+ "controller_rate_limit": {
+ "comment": "The maximum rate at which packets will be forwarded to the OpenFlow controller, in packets per second. If not specified, the default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}},
+ "controller_burst_limit": {
+ "comment": "The maximum number of unused packet credits that the bridge will allow to accumulate, in packets. If not specified, the default is implementation-specific.",
+ "type": {"key": "integer", "min": 0, "max": 1}}}},
+ "SSL": {
+ "comment": "SSL configuration for an Open_vSwitch.",
+ "columns": {
+ "private_key": {
+ "comment": "Name of a PEM file containing the private key used as the switch's identity for SSL connections to the controller.",
+ "type": "string"},
+ "certificate": {
+ "comment": "Name of a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the switch's private key, identifying a trustworthy switch.",
+ "type": "string"},
+ "ca_cert": {
+ "comment": "Name of a PEM file containing the CA certificate used to verify that the switch is connected to a trustworthy controller.",
+ "type": "string"}}}}}
+++ /dev/null
-//
-// This is an ovsdb-idl schema. The OVSDB IDL compiler, ovsdb-idlc,
-// can translate it into an OVSDB schema (which simply entails
-// deleting some members from the schema) or C headers or source for
-// use with the IDL at runtime.
-//
-
-{"name": "ovs_vswitchd_db",
- "comment": "Configuration for one Open vSwitch daemon.",
- "tables": {
- "Open_vSwitch": {
- "comment": "Configuration for an Open vSwitch daemon.",
- "columns": {
- "bridges": {
- "comment": "Set of bridges managed by the daemon.",
- "type": {"key": "uuid", "keyRefTable": "Bridge",
- "min": 0, "max": "unlimited"}},
- "management_id": {
- "comment": "Exactly 12 hex digits that identify the daemon.",
- "type": "string"},
- "controller": {
- "comment": "Default Controller used by bridges.",
- "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}},
- "ssl": {
- "comment": "SSL used globally by the daemon.",
- "type": {"key": "uuid", "keyRefTable": "SSL", "min": 0, "max": 1}}}},
- "Bridge": {
- "comment": "Configuration for a bridge within an Open_vSwitch.",
- "columns": {
- "name": {
- "comment": "Bridge identifier. Should be alphanumeric and no more than about 8 bytes long. Must be unique among the names of ports, interfaces, and bridges on a host.",
- "type": "string"},
- "datapath_id": {
- "comment": "OpenFlow datapath ID. Exactly 12 hex digits.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "hwaddr": {
- "comment": "Ethernet address to use for bridge. Exactly 12 hex digits in the form XX:XX:XX:XX:XX:XX.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "ports": {
- "comment": "Ports included in the bridge.",
- "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
- "mirrors": {
- "comment": "Port mirroring configuration.",
- "type": {"key": "uuid", "keyRefTable": "Mirror", "min": 0, "max": "unlimited"}},
- "netflow": {
- "comment": "NetFlow configuration.",
- "type": {"key": "uuid", "keyRefTable": "NetFlow", "min": 0, "max": "unlimited"}},
- "controller": {
- "comment": "OpenFlow controller. If unset, defaults to that specified by the parent Open_vSwitch.",
- "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}}}},
- "Port": {
- "comment": "A port within a Bridge. May contain a single Interface or multiple (bonded) Interfaces.",
- "columns": {
- "name": {
- "comment": "Port name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the interface name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host.",
- "type": "string"},
- "interfaces": {
- "comment": "The Port's Interfaces. If there is more than one, this is a bonded Port.",
- "type": {"key": "uuid", "keyRefTable": "Interface", "min": 1, "max": "unlimited"}},
- "trunks": {
- "comment": "The 802.1Q VLAN(s) that this port trunks. Should be empty if this port trunks all VLAN(s) or if this is not a trunk port.",
- "type": {"key": "integer", "min": 0, "max": 4096}},
- "tag": {
- "comment": "This port's implicitly tagged VLAN. Should be empty if this is a trunk port.",
- "type": {"key": "integer", "min": 0, "max": 1}},
- "updelay": {
- "comment": "For a bonded port, the number of milliseconds for which carrier must stay up on an interface before the interface is considered to be up. Ignored for non-bonded ports.",
- "type": "integer"},
- "downdelay": {
- "comment": "For a bonded port, the number of milliseconds for which carrier must stay down on an interface before the interface is considered to be down. Ignored for non-bonded ports.",
- "type": "integer"}}},
- "Interface": {
- "comment": "An interface within a Port.",
- "columns": {
- "name": {
- "comment": "Interface name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the port name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host.",
- "type": "string"},
- "internal": {
- "comment": "An \"internal\" port is one that is implemented in software as a logical device.",
- "type": "boolean"},
- "ingress_policing_rate": {
- "comment": "Maximum rate for data received on this interface, in kbps. Set to 0 to disable policing.",
- "type": "integer"},
- "ingress_policing_burst": {
- "comment": "Maximum burst size for data received on this interface, in kb. The default burst size if set to 0 is 10 kb.",
- "type": "integer"}}},
- "Mirror": {
- "comment": "A port mirror within a Bridge.",
- "columns": {
- "name": {
- "comment": "Arbitrary identifier for the Mirror.",
- "type": "string"},
- "select_src_port": {
- "comment": "Ports on which arriving packets are selected for mirroring.",
- "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
- "select_dst_port": {
- "comment": "Ports on which departing packets are selected for mirroring.",
- "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": "unlimited"}},
- "select_vlan": {
- "comment": "VLANs on which packets are selected for mirroring.",
- "type": {"key": "integer", "min": 0, "max": 4096}},
- "output_port": {
- "comment": "Output port for selected packets. Mutually exclusive with output_vlan.",
- "type": {"key": "uuid", "keyRefTable": "Port", "min": 0, "max": 1}},
- "output_vlan": {
- "comment": "Output VLAN for selected packets. Mutually exclusive with output_port.",
- "type": {"key": "integer", "min": 0, "max": 1}}}},
- "NetFlow": {
- "comment": "A NetFlow target.",
- "columns": {
- "target": {
- "comment": "NetFlow target in the form \"IP:PORT\".",
- "type": "string"},
- "engine_type": {
- "comment": "Engine type to use in NetFlow messages. Defaults to datapath index if not specified.",
- "type": "integer", "min":0, "max":1},
- "engine_id": {
- "comment": "Engine ID to use in NetFlow messages. Defaults to datapath index if not specified.",
- "type": "integer", "min":0, "max":1},
- "add_id_to_interface": {
- "comment": "Place least-significant 7 bits of engine ID into most significant bits of ingress and egress interface fields of NetFlow records?",
- "type": "boolean"}}},
- "Controller": {
- "comment": "An OpenFlow controller.",
- "columns": {
- "target": {
- "comment": "Connection method for controller, e.g. \"ssl:...\", \"tcp:...\". The special string \"discover\" enables controller discovery.",
- "type": "string"},
- "max_backoff": {
- "comment": "Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}},
- "inactivity_probe": {
- "comment": "Maximum number of milliseconds of idle time on connection to controller before sending an inactivity probe message. Default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}},
- "fail_mode": {
- "comment": "Either \"standalone\" or \"secure\", or empty to use the implementation's default.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "discover_accept_regex": {
- "comment": "If \"target\" is \"discover\", a POSIX extended regular expression against which the discovered controller location is validated. If not specified, the default is implementation-specific.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "discover_update_resolv_conf": {
- "comment": "If \"target\" is \"discover\", whether to update /etc/resolv.conf when the controller is discovered. If not specified, the default is implementation-specific.",
- "type": {"key": "boolean", "min": 0, "max": 1}},
- "connection_mode": {
- "comment": "Either \"in-band\" or \"out-of-band\". If not specified, the default is implementation-specific.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "local_ip": {
- "comment": "If \"target\" is not \"discover\", the IP address to configure on the local port.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "local_netmask": {
- "comment": "If \"target\" is not \"discover\", the IP netmask to configure on the local port.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "local_gateway": {
- "comment": "If \"target\" is not \"discover\", the IP gateway to configure on the local port.",
- "type": {"key": "string", "min": 0, "max": 1}},
- "controller_rate_limit": {
- "comment": "The maximum rate at which packets will be forwarded to the OpenFlow controller, in packets per second. If not specified, the default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}},
- "controller_burst_limit": {
- "comment": "The maximum number of unused packet credits that the bridge will allow to accumulate, in packets. If not specified, the default is implementation-specific.",
- "type": {"key": "integer", "min": 0, "max": 1}}}},
- "SSL": {
- "comment": "SSL configuration for an Open_vSwitch.",
- "columns": {
- "private_key": {
- "comment": "Name of a PEM file containing the private key used as the switch's identity for SSL connections to the controller.",
- "type": "string"},
- "certificate": {
- "comment": "Name of a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the switch's private key, identifying a trustworthy switch.",
- "type": "string"},
- "ca_cert": {
- "comment": "Name of a PEM file containing the CA certificate used to verify that the switch is connected to a trustworthy controller.",
- "type": "string"}}}}}