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:
@@ -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)
|
||||
|
1010
python/ovs/db/idl.py
1010
python/ovs/db/idl.py
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
@@ -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]
|
||||
|
Reference in New Issue
Block a user