-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
from ovs.db import error
import ovs.db.types
+
class ConstraintViolation(error.Error):
def __init__(self, msg, json=None):
error.Error.__init__(self, msg, json, tag="constraint violation")
+
def escapeCString(src):
dst = []
for c in src:
dst.append(c)
return ''.join(dst)
+
def returnUnchanged(x):
return x
+
class Atom(object):
def __init__(self, type_, value=None):
self.type = type_
@staticmethod
def default(type_):
+ """Returns the default value for the given type_, which must be an
+ instance of ovs.db.types.AtomicType.
+
+ The default value for each atomic type is;
+
+ - 0, for integer or real atoms.
+
+ - False, for a boolean atom.
+
+ - "", for a string atom.
+
+ - The all-zeros UUID, for a UUID atom."""
return Atom(type_)
def is_default(self):
type_ = base.type
json = ovs.db.parser.float_to_int(json)
if ((type_ == ovs.db.types.IntegerType and type(json) in [int, long])
- or (type_ == ovs.db.types.RealType and type(json) in [int, long, float])
+ or (type_ == ovs.db.types.RealType
+ and type(json) in [int, long, float])
or (type_ == ovs.db.types.BooleanType and type(json) == bool)
- or (type_ == ovs.db.types.StringType and type(json) in [str, unicode])):
+ or (type_ == ovs.db.types.StringType
+ and type(json) in [str, unicode])):
atom = Atom(type_, json)
elif type_ == ovs.db.types.UuidType:
atom = Atom(type_, ovs.ovsuuid.from_json(json, symtab))
atom.check_constraints(base)
return atom
+ @staticmethod
+ def from_python(base, value):
+ value = ovs.db.parser.float_to_int(value)
+ if type(value) in base.type.python_types:
+ atom = Atom(base.type, value)
+ else:
+ raise error.Error("expected %s, got %s" % (base.type, type(value)))
+ atom.check_constraints(base)
+ return atom
+
def check_constraints(self, base):
"""Checks whether 'atom' meets the constraints (if any) defined in
'base' and raises an ovs.db.error.Error if any constraint is violated.
'base' and 'atom' must have the same type.
-
Checking UUID constraints is deferred to transaction commit time, so
this function does nothing for UUID constraints."""
assert base.type == self.type
raise ConstraintViolation(
'"%s" length %d is greater than maximum allowed '
'length %d' % (s, length, base.max_length))
-
+
def to_json(self):
if self.type == ovs.db.types.UuidType:
return ovs.ovsuuid.to_json(self.value)
return self.value.value
__need_quotes_re = re.compile("$|true|false|[^_a-zA-Z]|.*[^-._a-zA-Z]")
+
@staticmethod
def __string_needs_quotes(s):
return Atom.__need_quotes_re.match(s)
raise TypeError
return Atom(t, x)
+
class Datum(object):
def __init__(self, type_, values={}):
self.type = type_
"""Parses 'json' as a datum of the type described by 'type'. If
successful, returns a new datum. On failure, raises an
ovs.db.error.Error.
-
+
Violations of constraints expressed by 'type' are treated as errors.
-
+
If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted.
Refer to ovsdb/SPECS for information about this, and for the syntax
that this function accepts."""
return [[k.value, v.value] for k, v in self.values.iteritems()]
else:
return [k.value for k in self.values.iterkeys()]
-
+
+ def as_dict(self):
+ return dict(self.values)
+
def as_scalar(self):
if len(self.values) == 1:
if self.type.is_map():
else:
return None
+ def to_python(self, uuid_to_row):
+ """Returns this datum's value converted into a natural Python
+ representation of this datum's type, according to the following
+ rules:
+
+ - If the type has exactly one value and it is not a map (that is,
+ self.type.is_scalar() returns True), then the value is:
+
+ * An int or long, for an integer column.
+
+ * An int or long or float, for a real column.
+
+ * A bool, for a boolean column.
+
+ * A str or unicode object, for a string column.
+
+ * A uuid.UUID object, for a UUID column without a ref_table.
+
+ * An object represented the referenced row, for a UUID column with
+ a ref_table. (For the Idl, this object will be an ovs.db.idl.Row
+ object.)
+
+ If some error occurs (e.g. the database server's idea of the column
+ is different from the IDL's idea), then the default value for the
+ scalar type is used (see Atom.default()).
+
+ - Otherwise, if the type is not a map, then the value is a Python list
+ whose elements have the types described above.
+
+ - Otherwise, the type is a map, and the value is a Python dict that
+ maps from key to value, with key and value types determined as
+ described above.
+
+ 'uuid_to_row' must be a function that takes a value and an
+ ovs.db.types.BaseType and translates UUIDs into row objects."""
+ if self.type.is_scalar():
+ value = uuid_to_row(self.as_scalar(), self.type.key)
+ if value is None:
+ return self.type.key.default()
+ else:
+ return value
+ elif self.type.is_map():
+ value = {}
+ for k, v in self.values.iteritems():
+ dk = uuid_to_row(k.value, self.type.key)
+ dv = uuid_to_row(v.value, self.type.value)
+ if dk is not None and dv is not None:
+ value[dk] = dv
+ return value
+ else:
+ s = set()
+ for k in self.values:
+ dk = uuid_to_row(k.value, self.type.key)
+ if dk is not None:
+ s.add(dk)
+ return sorted(s)
+
+ @staticmethod
+ def from_python(type_, value, row_to_uuid):
+ """Returns a new Datum with the given ovs.db.types.Type 'type_'. The
+ new datum's value is taken from 'value', which must take the form
+ described as a valid return value from Datum.to_python() for 'type'.
+
+ Each scalar value within 'value' is initally passed through
+ 'row_to_uuid', which should convert objects that represent rows (if
+ any) into uuid.UUID objects and return other data unchanged.
+
+ Raises ovs.db.error.Error if 'value' is not in an appropriate form for
+ 'type_'."""
+ d = {}
+ if type(value) == dict:
+ for k, v in value.iteritems():
+ ka = Atom.from_python(type_.key, row_to_uuid(k))
+ va = Atom.from_python(type_.value, row_to_uuid(v))
+ d[ka] = va
+ elif type(value) in (list, tuple):
+ for k in value:
+ ka = Atom.from_python(type_.key, row_to_uuid(k))
+ d[ka] = None
+ else:
+ ka = Atom.from_python(type_.key, row_to_uuid(value))
+ d[ka] = None
+
+ datum = Datum(type_, d)
+ datum.check_constraints()
+ if not datum.conforms_to_type():
+ raise error.Error("%d values when type requires between %d and %d"
+ % (len(d), type_.n_min, type_.n_max))
+
+ return datum
+
def __getitem__(self, key):
if not isinstance(key, Atom):
key = Atom.new(key)
return self.values[key].value
else:
return default
-
+
def __str__(self):
return self.to_string()
for i, key in enumerate(sorted(self.values)):
s += key.cInitAtom("%s->keys[%d]" % (var, i))
-
+
if self.type.value:
s += ["%s->values = xmalloc(%d * sizeof *%s->values);"
% (var, len(self.values), var)]