Skip to content

Commit

Permalink
ovsdb-cs: New module that factors out code from ovsdb-idl.
Browse files Browse the repository at this point in the history
This new module has a single direct user now.  In the future, it
will also be used by OVN.

Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Ilya Maximets <i.maximets@ovn.org>
  • Loading branch information
blp committed Jan 21, 2021
1 parent e15ca90 commit d7a6f23
Show file tree
Hide file tree
Showing 4 changed files with 471 additions and 369 deletions.
2 changes: 2 additions & 0 deletions lib/automake.mk
Expand Up @@ -240,6 +240,8 @@ lib_libopenvswitch_la_SOURCES = \
lib/ovs-router.c \
lib/ovs-thread.c \
lib/ovs-thread.h \
lib/ovsdb-cs.c \
lib/ovsdb-cs.h \
lib/ovsdb-data.c \
lib/ovsdb-data.h \
lib/ovsdb-error.c \
Expand Down
326 changes: 326 additions & 0 deletions lib/ovsdb-cs.c
@@ -0,0 +1,326 @@
/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
* Copyright (C) 2016 Hewlett Packard Enterprise Development LP
*
* 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 CONDITION 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-cs.h"

#include <errno.h>

#include "hash.h"
#include "jsonrpc.h"
#include "openvswitch/dynamic-string.h"
#include "openvswitch/hmap.h"
#include "openvswitch/json.h"
#include "openvswitch/poll-loop.h"
#include "openvswitch/shash.h"
#include "openvswitch/vlog.h"
#include "ovsdb-data.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "ovsdb-session.h"
#include "ovsdb-types.h"
#include "sset.h"
#include "svec.h"
#include "util.h"
#include "uuid.h"

VLOG_DEFINE_THIS_MODULE(ovsdb_cs);

static void
log_error(struct ovsdb_error *error)
{
char *s = ovsdb_error_to_string_free(error);
VLOG_WARN("error parsing database schema: %s", s);
free(s);
}

/* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC
* 7047, to obtain the names of its rows and columns. If successful, returns
* an shash whose keys are table names and whose values are ssets, where each
* sset contains the names of its table's columns. On failure (due to a parse
* error), returns NULL.
*
* It would also be possible to use the general-purpose OVSDB schema parser in
* ovsdb-server, but that's overkill, possibly too strict for the current use
* case, and would require restructuring ovsdb-server to separate the schema
* code from the rest. */
struct shash *
ovsdb_cs_parse_schema(const struct json *schema_json)
{
struct ovsdb_parser parser;
const struct json *tables_json;
struct ovsdb_error *error;
struct shash_node *node;
struct shash *schema;

ovsdb_parser_init(&parser, schema_json, "database schema");
tables_json = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
error = ovsdb_parser_destroy(&parser);
if (error) {
log_error(error);
return NULL;
}

schema = xmalloc(sizeof *schema);
shash_init(schema);
SHASH_FOR_EACH (node, json_object(tables_json)) {
const char *table_name = node->name;
const struct json *json = node->data;
const struct json *columns_json;

ovsdb_parser_init(&parser, json, "table schema for table %s",
table_name);
columns_json = ovsdb_parser_member(&parser, "columns", OP_OBJECT);
error = ovsdb_parser_destroy(&parser);
if (error) {
log_error(error);
ovsdb_cs_free_schema(schema);
return NULL;
}

struct sset *columns = xmalloc(sizeof *columns);
sset_init(columns);

struct shash_node *node2;
SHASH_FOR_EACH (node2, json_object(columns_json)) {
const char *column_name = node2->name;
sset_add(columns, column_name);
}
shash_add(schema, table_name, columns);
}
return schema;
}

/* Frees 'schema', which is in the format returned by
* ovsdb_cs_parse_schema(). */
void
ovsdb_cs_free_schema(struct shash *schema)
{
if (schema) {
struct shash_node *node, *next;

SHASH_FOR_EACH_SAFE (node, next, schema) {
struct sset *sset = node->data;
sset_destroy(sset);
free(sset);
shash_delete(schema, node);
}
shash_destroy(schema);
free(schema);
}
}

static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
ovsdb_cs_parse_row_update1(const struct json *in,
struct ovsdb_cs_row_update *out)
{
const struct json *old_json, *new_json;

old_json = shash_find_data(json_object(in), "old");
new_json = shash_find_data(json_object(in), "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(in))) {
return ovsdb_syntax_error(in, NULL,
"<row-update> contains "
"unexpected member");
} else if (!old_json && !new_json) {
return ovsdb_syntax_error(in, NULL,
"<row-update> missing \"old\" "
"and \"new\" members");
}

if (!new_json) {
out->type = OVSDB_CS_ROW_DELETE;
out->columns = json_object(old_json);
} else if (!old_json) {
out->type = OVSDB_CS_ROW_INSERT;
out->columns = json_object(new_json);
} else {
out->type = OVSDB_CS_ROW_UPDATE;
out->columns = json_object(new_json);
}
return NULL;
}

static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
ovsdb_cs_parse_row_update2(const struct json *in,
struct ovsdb_cs_row_update *out)
{
const struct shash *object = json_object(in);
if (shash_count(object) != 1) {
return ovsdb_syntax_error(
in, NULL, "<row-update2> has %"PRIuSIZE" members "
"instead of expected 1", shash_count(object));
}

struct shash_node *node = shash_first(object);
const struct json *columns = node->data;
if (!strcmp(node->name, "insert") || !strcmp(node->name, "initial")) {
out->type = OVSDB_CS_ROW_INSERT;
} else if (!strcmp(node->name, "modify")) {
out->type = OVSDB_CS_ROW_XOR;
} else if (!strcmp(node->name, "delete")) {
out->type = OVSDB_CS_ROW_DELETE;
if (columns->type != JSON_NULL) {
return ovsdb_syntax_error(
in, NULL,
"<row-update2> delete operation has unexpected value");
}
return NULL;
} else {
return ovsdb_syntax_error(in, NULL,
"<row-update2> has unknown member \"%s\"",
node->name);
}

if (columns->type != JSON_OBJECT) {
return ovsdb_syntax_error(
in, NULL,
"<row-update2> \"%s\" operation has unexpected value",
node->name);
}
out->columns = json_object(columns);

return NULL;
}

static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
ovsdb_cs_parse_row_update(const char *table_name,
const struct json *in, int version,
struct ovsdb_cs_row_update *out)
{
if (in->type != JSON_OBJECT) {
const char *suffix = version > 1 ? "2" : "";
return ovsdb_syntax_error(
in, NULL,
"<table-update%s> for table \"%s\" contains <row-update%s> "
"that is not an object",
suffix, table_name, suffix);
}

return (version == 1
? ovsdb_cs_parse_row_update1(in, out)
: ovsdb_cs_parse_row_update2(in, out));
}

static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
ovsdb_cs_parse_table_update(const char *table_name,
const struct json *in, int version,
struct ovsdb_cs_table_update *out)
{
const char *suffix = version > 1 ? "2" : "";

if (in->type != JSON_OBJECT) {
return ovsdb_syntax_error(
in, NULL, "<table-update%s> for table \"%s\" is not an object",
suffix, table_name);
}
struct shash *in_rows = json_object(in);

out->row_updates = xmalloc(shash_count(in_rows) * sizeof *out->row_updates);

const struct shash_node *node;
SHASH_FOR_EACH (node, in_rows) {
const char *row_uuid_string = node->name;
struct uuid row_uuid;
if (!uuid_from_string(&row_uuid, row_uuid_string)) {
return ovsdb_syntax_error(
in, NULL,
"<table-update%s> for table \"%s\" contains "
"bad UUID \"%s\" as member name",
suffix, table_name, row_uuid_string);
}

const struct json *in_ru = node->data;
struct ovsdb_cs_row_update *out_ru = &out->row_updates[out->n++];
*out_ru = (struct ovsdb_cs_row_update) { .row_uuid = row_uuid };

struct ovsdb_error *error = ovsdb_cs_parse_row_update(
table_name, in_ru, version, out_ru);
if (error) {
return error;
}
}

return NULL;
}

/* Parses OVSDB <table-updates> or <table-updates2> object 'in' into '*outp'.
* If successful, sets '*outp' to the new object and returns NULL. On failure,
* returns the error and sets '*outp' to NULL.
*
* On success, the caller must eventually free '*outp', with
* ovsdb_cs_db_update_destroy().
*
* 'version' should be 1 if 'in' is a <table-updates>, 2 or 3 if it is a
* <table-updates2>. */
struct ovsdb_error * OVS_WARN_UNUSED_RESULT
ovsdb_cs_parse_db_update(const struct json *in, int version,
struct ovsdb_cs_db_update **outp)
{
const char *suffix = version > 1 ? "2" : "";

*outp = NULL;
if (in->type != JSON_OBJECT) {
return ovsdb_syntax_error(in, NULL,
"<table-updates%s> is not an object", suffix);
}

struct ovsdb_cs_db_update *out = xzalloc(sizeof *out);
out->table_updates = xmalloc(shash_count(json_object(in))
* sizeof *out->table_updates);
const struct shash_node *node;
SHASH_FOR_EACH (node, json_object(in)) {
const char *table_name = node->name;
const struct json *in_tu = node->data;

struct ovsdb_cs_table_update *out_tu = &out->table_updates[out->n++];
*out_tu = (struct ovsdb_cs_table_update) { .table_name = table_name };

struct ovsdb_error *error = ovsdb_cs_parse_table_update(
table_name, in_tu, version, out_tu);
if (error) {
ovsdb_cs_db_update_destroy(out);
return error;
}
}

*outp = out;
return NULL;
}

/* Frees 'du', which was presumably allocated by ovsdb_cs_parse_db_update(). */
void
ovsdb_cs_db_update_destroy(struct ovsdb_cs_db_update *du)
{
if (!du) {
return;
}

for (size_t i = 0; i < du->n; i++) {
struct ovsdb_cs_table_update *tu = &du->table_updates[i];
free(tu->row_updates);
}
free(du->table_updates);
free(du);
}

0 comments on commit d7a6f23

Please sign in to comment.