2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00
ovs/lib/ovsdb-types.c

765 lines
22 KiB
C
Raw Normal View History

/* Copyright (c) 2009, 2010, 2011, 2013 Nicira, Inc.
2009-11-04 15:11:44 -08:00
*
* 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"
2010-02-08 14:09:36 -08:00
#include <float.h>
2009-11-04 15:11:44 -08:00
#include <limits.h>
#include "openvswitch/dynamic-string.h"
#include "openvswitch/json.h"
#include "ovs-thread.h"
#include "ovsdb-data.h"
2009-11-04 15:11:44 -08:00
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "util.h"
2009-11-04 15:11:44 -08:00
const struct ovsdb_type ovsdb_type_integer =
2010-02-08 14:09:36 -08:00
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_INTEGER_INIT);
2009-11-04 15:11:44 -08:00
const struct ovsdb_type ovsdb_type_real =
2010-02-08 14:09:36 -08:00
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_REAL_INIT);
2009-11-04 15:11:44 -08:00
const struct ovsdb_type ovsdb_type_boolean =
2010-02-08 14:09:36 -08:00
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_BOOLEAN_INIT);
2009-11-04 15:11:44 -08:00
const struct ovsdb_type ovsdb_type_string =
2010-02-08 14:09:36 -08:00
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_STRING_INIT);
2009-11-04 15:11:44 -08:00
const struct ovsdb_type ovsdb_type_uuid =
2010-02-08 14:09:36 -08:00
OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_UUID_INIT);
/* ovsdb_atomic_type */
2009-11-04 15:11:44 -08:00
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_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");
}
}
2010-02-08 14:09:36 -08:00
/* ovsdb_base_type */
void
ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type)
{
base->type = type;
base->enum_ = NULL;
2010-02-08 14:09:36 -08:00
switch (base->type) {
case OVSDB_TYPE_VOID:
break;
case OVSDB_TYPE_INTEGER:
base->integer.min = INT64_MIN;
base->integer.max = INT64_MAX;
2010-02-08 14:09:36 -08:00
break;
case OVSDB_TYPE_REAL:
base->real.min = -DBL_MAX;
base->real.max = DBL_MAX;
2010-02-08 14:09:36 -08:00
break;
case OVSDB_TYPE_BOOLEAN:
break;
case OVSDB_TYPE_STRING:
base->string.minLen = 0;
base->string.maxLen = UINT_MAX;
2010-02-08 14:09:36 -08:00
break;
case OVSDB_TYPE_UUID:
base->uuid.refTableName = NULL;
base->uuid.refTable = NULL;
ovsdb-types: Fix use of uninitialized reference type. Types for UUID columns that are not references (e.g. version column) may end up with the refType not initialized. At the same time, some functions use the value during the type comparison, which may result in a false negative type check: WARNING: MemorySanitizer: use-of-uninitialized-value 0 0x75abf7 in ovsdb_base_type_equals lib/ovsdb-types.c:321:16 1 0x75f899 in ovsdb_type_equals lib/ovsdb-types.c:626:12 2 0x4fc607 in ovsdb_convert_table ovsdb/file.c:316:13 3 0x4fb5ec in ovsdb_convert ovsdb/file.c:388:17 4 0x598bcf in ovsdb_trigger_try ovsdb/trigger.c:313:25 5 0x596902 in ovsdb_trigger_init ovsdb/trigger.c:66:12 6 0x5189e8 in ovsdb_jsonrpc_trigger_create ovsdb/jsonrpc-server.c:1247:27 7 0x51473d in ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1112:13 8 0x512ea2 in ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:677:17 9 0x50d67d in ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:700:21 10 0x50cc49 in ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:522:9 11 0x4d756f in main_loop ovsdb/ovsdb-server.c:330:9 12 0x4c4e52 in main ovsdb/ovsdb-server.c:890:5 13 0x7f42bb in __libc_start_call_main 14 0x7f42bb in __libc_start_main@GLIBC_2.2.5 15 0x432b64 in _start (ovsdb/ovsdb-server+0x432b64) Fix the initialization function to always set the refType to some value. Using the "strong" as we don't have an UNSPEC value in the enumeration. It will not be meaningful unless the refTable is also set, in which case it will be properly updated, we just need the value to be the same for all the columns that are not references. Fixes: e0e4266a90a2 ("ovsdb-types: Add functions to compare types for equality.") Acked-by: Mike Pattrick <mkp@redhat.com> Acked-by: Eelco Chaudron <echaudro@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2024-11-28 17:00:36 +01:00
base->uuid.refType = OVSDB_REF_STRONG;
2010-02-08 14:09:36 -08:00
break;
case OVSDB_N_TYPES:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
default:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
}
}
/* Returns the type of the 'enum_' member for an ovsdb_base_type whose 'type'
* is 'atomic_type'. */
const struct ovsdb_type *
ovsdb_base_type_get_enum_type(enum ovsdb_atomic_type atomic_type)
{
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
static struct ovsdb_type *types[OVSDB_N_TYPES];
if (ovsthread_once_start(&once)) {
enum ovsdb_atomic_type i;
for (i = 0; i < OVSDB_N_TYPES; i++) {
struct ovsdb_type *type;
types[i] = type = xmalloc(sizeof *type);
ovsdb_base_type_init(&type->key, i);
ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID);
type->n_min = 1;
type->n_max = UINT_MAX;
}
ovsthread_once_done(&once);
}
return types[atomic_type];
}
2010-02-08 14:09:36 -08:00
void
ovsdb_base_type_clone(struct ovsdb_base_type *dst,
const struct ovsdb_base_type *src)
{
*dst = *src;
if (src->enum_) {
dst->enum_ = xmalloc(sizeof *dst->enum_);
ovsdb_datum_clone(dst->enum_, src->enum_);
}
2010-02-08 14:09:36 -08:00
switch (dst->type) {
case OVSDB_TYPE_VOID:
case OVSDB_TYPE_INTEGER:
case OVSDB_TYPE_REAL:
case OVSDB_TYPE_BOOLEAN:
break;
case OVSDB_TYPE_STRING:
break;
case OVSDB_TYPE_UUID:
if (dst->uuid.refTableName) {
dst->uuid.refTableName = xstrdup(dst->uuid.refTableName);
}
2010-02-08 14:09:36 -08:00
break;
case OVSDB_N_TYPES:
default:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
}
}
void
ovsdb_base_type_destroy(struct ovsdb_base_type *base)
{
if (base) {
if (base->enum_) {
ovsdb_datum_destroy(base->enum_,
ovsdb_base_type_get_enum_type(base->type));
free(base->enum_);
}
2010-02-08 14:09:36 -08:00
switch (base->type) {
case OVSDB_TYPE_VOID:
case OVSDB_TYPE_INTEGER:
case OVSDB_TYPE_REAL:
case OVSDB_TYPE_BOOLEAN:
break;
case OVSDB_TYPE_STRING:
break;
case OVSDB_TYPE_UUID:
free(base->uuid.refTableName);
2010-02-08 14:09:36 -08:00
break;
case OVSDB_N_TYPES:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
default:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
}
}
}
bool
ovsdb_base_type_is_valid(const struct ovsdb_base_type *base)
{
switch (base->type) {
case OVSDB_TYPE_VOID:
return true;
case OVSDB_TYPE_INTEGER:
return base->integer.min <= base->integer.max;
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_REAL:
return base->real.min <= base->real.max;
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_BOOLEAN:
return true;
case OVSDB_TYPE_STRING:
return base->string.minLen <= base->string.maxLen;
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_UUID:
return true;
case OVSDB_N_TYPES:
default:
return false;
}
}
bool
ovsdb_base_type_equals(const struct ovsdb_base_type *a,
const struct ovsdb_base_type *b)
{
if (a == b) {
return true;
}
if (a->type != b->type) {
return false;
}
if ((a->enum_ && !b->enum_) || (!a->enum_ && b->enum_)) {
return false;
} else if (a->enum_ &&
!ovsdb_datum_equals(a->enum_, b->enum_,
ovsdb_base_type_get_enum_type(a->type))) {
return false;
}
switch (a->type) {
case OVSDB_TYPE_VOID:
return true;
case OVSDB_TYPE_INTEGER:
return a->integer.min == b->integer.min
&& a->integer.max == b->integer.max;
case OVSDB_TYPE_REAL:
return a->real.min == b->real.min && a->real.max == b->real.max;
case OVSDB_TYPE_BOOLEAN:
return true;
case OVSDB_TYPE_STRING:
return a->string.minLen == b->string.minLen
&& a->string.maxLen == b->string.maxLen;
case OVSDB_TYPE_UUID:
/* Not comparing the table pointer here, only the table name, as this
* function can be used to compare types from different databases, so
* pointers will be different. */
return a->uuid.refType == b->uuid.refType
&& nullable_string_is_equal(a->uuid.refTableName,
b->uuid.refTableName);
case OVSDB_N_TYPES:
default:
OVS_NOT_REACHED();
}
}
2010-02-08 14:09:36 -08:00
bool
ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base)
{
if (base->enum_) {
return true;
}
2010-02-08 14:09:36 -08:00
switch (base->type) {
case OVSDB_TYPE_VOID:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_INTEGER:
return (base->integer.min != INT64_MIN
|| base->integer.max != INT64_MAX);
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_REAL:
return (base->real.min != -DBL_MAX
|| base->real.max != DBL_MAX);
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_BOOLEAN:
return false;
case OVSDB_TYPE_STRING:
return base->string.minLen != 0 || base->string.maxLen != UINT_MAX;
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_UUID:
return base->uuid.refTableName != NULL;
2010-02-08 14:09:36 -08:00
case OVSDB_N_TYPES:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
default:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
}
}
void
ovsdb_base_type_clear_constraints(struct ovsdb_base_type *base)
{
enum ovsdb_atomic_type type = base->type;
ovsdb_base_type_destroy(base);
ovsdb_base_type_init(base, type);
}
static struct ovsdb_error *
parse_optional_uint(struct ovsdb_parser *parser, const char *member,
unsigned int *uint)
{
const struct json *json;
json = ovsdb_parser_member(parser, member, OP_INTEGER | OP_OPTIONAL);
if (json) {
if (json->integer < 0 || json->integer > UINT_MAX) {
2010-02-08 14:09:36 -08:00
return ovsdb_syntax_error(json, NULL,
"%s out of valid range 0 to %u",
member, UINT_MAX);
}
*uint = json->integer;
2010-02-08 14:09:36 -08:00
}
return NULL;
}
struct ovsdb_error *
ovsdb_base_type_from_json(struct ovsdb_base_type *base,
const struct json *json)
{
struct ovsdb_parser parser;
struct ovsdb_error *error;
const struct json *type, *enum_;
2010-02-08 14:09:36 -08:00
if (json->type == JSON_STRING) {
error = ovsdb_atomic_type_from_json(&base->type, json);
if (error) {
return error;
}
ovsdb_base_type_init(base, base->type);
return NULL;
}
ovsdb_parser_init(&parser, json, "ovsdb type");
type = ovsdb_parser_member(&parser, "type", OP_STRING);
if (ovsdb_parser_has_error(&parser)) {
base->type = OVSDB_TYPE_VOID;
return ovsdb_parser_finish(&parser);
}
error = ovsdb_atomic_type_from_json(&base->type, type);
if (error) {
ovsdb_error_destroy(ovsdb_parser_destroy(&parser));
2010-02-08 14:09:36 -08:00
return error;
}
ovsdb_base_type_init(base, base->type);
enum_ = ovsdb_parser_member(&parser, "enum", OP_ANY | OP_OPTIONAL);
if (enum_) {
base->enum_ = xmalloc(sizeof *base->enum_);
error = ovsdb_datum_from_json(
base->enum_, ovsdb_base_type_get_enum_type(base->type),
enum_, NULL);
if (error) {
free(base->enum_);
base->enum_ = NULL;
}
} else if (base->type == OVSDB_TYPE_INTEGER) {
2010-02-08 14:09:36 -08:00
const struct json *min, *max;
min = ovsdb_parser_member(&parser, "minInteger",
OP_INTEGER | OP_OPTIONAL);
max = ovsdb_parser_member(&parser, "maxInteger",
OP_INTEGER | OP_OPTIONAL);
base->integer.min = min ? min->integer : INT64_MIN;
base->integer.max = max ? max->integer : INT64_MAX;
if (base->integer.min > base->integer.max) {
2010-02-08 14:09:36 -08:00
error = ovsdb_syntax_error(json, NULL,
"minInteger exceeds maxInteger");
}
} else if (base->type == OVSDB_TYPE_REAL) {
const struct json *min, *max;
min = ovsdb_parser_member(&parser, "minReal", OP_NUMBER | OP_OPTIONAL);
max = ovsdb_parser_member(&parser, "maxReal", OP_NUMBER | OP_OPTIONAL);
base->real.min = min ? json_real(min) : -DBL_MAX;
base->real.max = max ? json_real(max) : DBL_MAX;
if (base->real.min > base->real.max) {
2010-02-08 14:09:36 -08:00
error = ovsdb_syntax_error(json, NULL, "minReal exceeds maxReal");
}
} else if (base->type == OVSDB_TYPE_STRING) {
if (!error) {
error = parse_optional_uint(&parser, "minLength",
&base->string.minLen);
2010-02-08 14:09:36 -08:00
}
if (!error) {
error = parse_optional_uint(&parser, "maxLength",
&base->string.maxLen);
2010-02-08 14:09:36 -08:00
}
if (!error && base->string.minLen > base->string.maxLen) {
2010-02-08 14:09:36 -08:00
error = ovsdb_syntax_error(json, NULL,
"minLength exceeds maxLength");
}
} else if (base->type == OVSDB_TYPE_UUID) {
const struct json *refTable;
refTable = ovsdb_parser_member(&parser, "refTable",
OP_ID | OP_OPTIONAL);
if (refTable) {
const struct json *refType;
base->uuid.refTableName = xstrdup(refTable->string);
/* We can't set base->uuid.refTable here because we don't have
* enough context (we might not even be running in ovsdb-server).
* ovsdb_create() will set refTable later. */
refType = ovsdb_parser_member(&parser, "refType",
OP_ID | OP_OPTIONAL);
if (refType) {
const char *refType_s = json_string(refType);
if (!strcmp(refType_s, "strong")) {
base->uuid.refType = OVSDB_REF_STRONG;
} else if (!strcmp(refType_s, "weak")) {
base->uuid.refType = OVSDB_REF_WEAK;
} else {
error = ovsdb_syntax_error(json, NULL, "refType must be "
"\"strong\" or \"weak\" (not "
"\"%s\")", refType_s);
}
} else {
base->uuid.refType = OVSDB_REF_STRONG;
}
}
2010-02-08 14:09:36 -08:00
}
if (error) {
ovsdb_error_destroy(ovsdb_parser_finish(&parser));
} else {
error = ovsdb_parser_finish(&parser);
}
if (error) {
ovsdb_base_type_destroy(base);
base->type = OVSDB_TYPE_VOID;
}
return error;
}
struct json *
ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
{
struct json *json;
if (!ovsdb_base_type_has_constraints(base)) {
return json_string_create(ovsdb_atomic_type_to_string(base->type));
}
json = json_object_create();
json_object_put_string(json, "type",
ovsdb_atomic_type_to_string(base->type));
if (base->enum_) {
const struct ovsdb_type *type;
type = ovsdb_base_type_get_enum_type(base->type);
json_object_put(json, "enum", ovsdb_datum_to_json(base->enum_, type));
}
2010-02-08 14:09:36 -08:00
switch (base->type) {
case OVSDB_TYPE_VOID:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
case OVSDB_TYPE_INTEGER:
if (base->integer.min != INT64_MIN) {
2010-02-08 14:09:36 -08:00
json_object_put(json, "minInteger",
json_integer_create(base->integer.min));
2010-02-08 14:09:36 -08:00
}
if (base->integer.max != INT64_MAX) {
2010-02-08 14:09:36 -08:00
json_object_put(json, "maxInteger",
json_integer_create(base->integer.max));
2010-02-08 14:09:36 -08:00
}
break;
case OVSDB_TYPE_REAL:
if (base->real.min != -DBL_MAX) {
2010-02-08 14:09:36 -08:00
json_object_put(json, "minReal",
json_real_create(base->real.min));
2010-02-08 14:09:36 -08:00
}
if (base->real.max != DBL_MAX) {
2010-02-08 14:09:36 -08:00
json_object_put(json, "maxReal",
json_real_create(base->real.max));
2010-02-08 14:09:36 -08:00
}
break;
case OVSDB_TYPE_BOOLEAN:
break;
case OVSDB_TYPE_STRING:
if (base->string.minLen != 0) {
2010-02-08 14:09:36 -08:00
json_object_put(json, "minLength",
json_integer_create(base->string.minLen));
2010-02-08 14:09:36 -08:00
}
if (base->string.maxLen != UINT_MAX) {
2010-02-08 14:09:36 -08:00
json_object_put(json, "maxLength",
json_integer_create(base->string.maxLen));
2010-02-08 14:09:36 -08:00
}
break;
case OVSDB_TYPE_UUID:
if (base->uuid.refTableName) {
json_object_put_string(json, "refTable",
base->uuid.refTableName);
if (base->uuid.refType == OVSDB_REF_WEAK) {
json_object_put_string(json, "refType", "weak");
}
}
2010-02-08 14:09:36 -08:00
break;
case OVSDB_N_TYPES:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
default:
OVS_NOT_REACHED();
2010-02-08 14:09:36 -08:00
}
return json;
}
/* ovsdb_type */
void
ovsdb_type_clone(struct ovsdb_type *dst, const struct ovsdb_type *src)
{
ovsdb_base_type_clone(&dst->key, &src->key);
ovsdb_base_type_clone(&dst->value, &src->value);
dst->n_min = src->n_min;
dst->n_max = src->n_max;
}
void
ovsdb_type_destroy(struct ovsdb_type *type)
{
ovsdb_base_type_destroy(&type->key);
ovsdb_base_type_destroy(&type->value);
}
bool
ovsdb_type_is_valid(const struct ovsdb_type *type)
{
return (type->key.type != OVSDB_TYPE_VOID
&& ovsdb_base_type_is_valid(&type->key)
&& ovsdb_base_type_is_valid(&type->value)
&& type->n_min <= 1
&& type->n_max >= 1);
2010-02-08 14:09:36 -08:00
}
2009-11-04 15:11:44 -08:00
bool
ovsdb_type_equals(const struct ovsdb_type *a, const struct ovsdb_type *b)
{
return ovsdb_base_type_equals(&a->key, &b->key)
&& ovsdb_base_type_equals(&a->value, &b->value)
&& a->n_min == b->n_min
&& a->n_max == b->n_max;
}
2009-11-04 15:11:44 -08:00
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->integer >= 0 && json->integer < UINT_MAX) {
*n = json->integer;
2009-11-04 15:11:44 -08:00
return NULL;
} else {
return ovsdb_syntax_error(json, NULL, "bad min or max value");
}
}
char *
ovsdb_type_to_english(const struct ovsdb_type *type)
{
2010-02-08 14:09:36 -08:00
const char *key = ovsdb_atomic_type_to_string(type->key.type);
const char *value = ovsdb_atomic_type_to_string(type->value.type);
2009-11-04 15:11:44 -08:00
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)
{
ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID);
2009-11-04 15:11:44 -08:00
type->n_min = 1;
type->n_max = 1;
if (json->type == JSON_STRING) {
2010-02-08 14:09:36 -08:00
return ovsdb_base_type_from_json(&type->key, json);
2009-11-04 15:11:44 -08:00
} 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");
2010-02-08 14:09:36 -08:00
key = ovsdb_parser_member(&parser, "key", OP_STRING | OP_OBJECT);
value = ovsdb_parser_member(&parser, "value",
OP_STRING | OP_OBJECT | OP_OPTIONAL);
2009-11-04 15:11:44 -08:00
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;
}
2010-02-08 14:09:36 -08:00
error = ovsdb_base_type_from_json(&type->key, key);
2009-11-04 15:11:44 -08:00
if (error) {
return error;
}
if (value) {
2010-02-08 14:09:36 -08:00
error = ovsdb_base_type_from_json(&type->value, value);
2009-11-04 15:11:44 -08:00
if (error) {
return error;
}
}
error = n_from_json(min, &type->n_min);
if (error) {
return error;
}
if (max && max->type == JSON_STRING
&& !strcmp(max->string, "unlimited")) {
2009-11-04 15:11:44 -08:00
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)
{
2010-02-08 14:09:36 -08:00
if (ovsdb_type_is_scalar(type)
&& !ovsdb_base_type_has_constraints(&type->key)) {
return ovsdb_base_type_to_json(&type->key);
2009-11-04 15:11:44 -08:00
} else {
struct json *json = json_object_create();
2010-02-08 14:09:36 -08:00
json_object_put(json, "key", ovsdb_base_type_to_json(&type->key));
if (type->value.type != OVSDB_TYPE_VOID) {
2009-11-04 15:11:44 -08:00
json_object_put(json, "value",
2010-02-08 14:09:36 -08:00
ovsdb_base_type_to_json(&type->value));
2009-11-04 15:11:44 -08:00
}
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;
}
}