2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 06:15:47 +00:00

python: Implement write support in Python IDL for OVSDB.

Until now, the Python bindings for OVSDB have not supported writing to the
database.  Instead, writes had to be done with "ovs-vsctl" subprocesses.
This commit adds write support and brings the Python bindings in line with
the C bindings.

This commit deletes the Python-specific IDL tests in favor of using the
same tests as the C version of the IDL, which now pass with both
implementations.

This commit updates the two users of the Python IDL to use the new write
support.  I tested this updates only by writing unit tests for them,
which appear in upcoming commits.
This commit is contained in:
Ben Pfaff
2011-09-21 10:43:03 -07:00
parent 7cba02e442
commit 8cdf034974
14 changed files with 1362 additions and 482 deletions

View File

@@ -81,6 +81,18 @@ class Atom(object):
@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):
@@ -102,12 +114,21 @@ class Atom(object):
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
@@ -363,6 +384,9 @@ class Datum(object):
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():
@@ -373,6 +397,97 @@ class Datum(object):
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)

File diff suppressed because it is too large Load Diff

View File

@@ -95,6 +95,9 @@ class DbSchema(object):
json["version"] = self.version
return json
def copy(self):
return DbSchema.from_json(self.to_json())
def __follow_ref_table(self, column, base, base_name):
if not base or base.type != types.UuidType or not base.ref_table_name:
return

View File

@@ -13,6 +13,7 @@
# limitations under the License.
import sys
import uuid
from ovs.db import error
import ovs.db.parser
@@ -20,9 +21,10 @@ import ovs.db.data
import ovs.ovsuuid
class AtomicType(object):
def __init__(self, name, default):
def __init__(self, name, default, python_types):
self.name = name
self.default = default
self.python_types = python_types
@staticmethod
def from_string(s):
@@ -51,12 +53,12 @@ class AtomicType(object):
def default_atom(self):
return ovs.db.data.Atom(self, self.default)
VoidType = AtomicType("void", None)
IntegerType = AtomicType("integer", 0)
RealType = AtomicType("real", 0.0)
BooleanType = AtomicType("boolean", False)
StringType = AtomicType("string", "")
UuidType = AtomicType("uuid", ovs.ovsuuid.zero())
VoidType = AtomicType("void", None, ())
IntegerType = AtomicType("integer", 0, (int, long))
RealType = AtomicType("real", 0.0, (int, long, float))
BooleanType = AtomicType("boolean", False, (bool,))
StringType = AtomicType("string", "", (str, unicode))
UuidType = AtomicType("uuid", ovs.ovsuuid.zero(), (uuid.UUID,))
ATOMIC_TYPES = [VoidType, IntegerType, RealType, BooleanType, StringType,
UuidType]