X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=python%2Fovs%2Fdb%2Fdata.py;h=172d5521c2192fb2c8a240955d85cdc4e29a98b3;hb=8cdf0349740c3e1a73af9aa6209bb22be952cd37;hp=ae8af2faa36b3d176ffc6e40a744eb2d314d618b;hpb=57d6a4c71bd0b4ed477ec273d4d4f84c5e4ed465;p=sliver-openvswitch.git diff --git a/python/ovs/db/data.py b/python/ovs/db/data.py index ae8af2faa..172d5521c 100644 --- a/python/ovs/db/data.py +++ b/python/ovs/db/data.py @@ -12,12 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import errno -import logging -import os import re -import select -import sys import uuid import ovs.poller @@ -35,41 +30,41 @@ class ConstraintViolation(error.Error): error.Error.__init__(self, msg, json, tag="constraint violation") def escapeCString(src): - dst = "" + dst = [] for c in src: if c in "\\\"": - dst += "\\" + c + dst.append("\\" + c) elif ord(c) < 32: if c == '\n': - dst += '\\n' + dst.append('\\n') elif c == '\r': - dst += '\\r' + dst.append('\\r') elif c == '\a': - dst += '\\a' + dst.append('\\a') elif c == '\b': - dst += '\\b' + dst.append('\\b') elif c == '\f': - dst += '\\f' + dst.append('\\f') elif c == '\t': - dst += '\\t' + dst.append('\\t') elif c == '\v': - dst += '\\v' + dst.append('\\v') else: - dst += '\\%03o' % ord(c) + dst.append('\\%03o' % ord(c)) else: - dst += c - return dst + dst.append(c) + return ''.join(dst) def returnUnchanged(x): return x class Atom(object): - def __init__(self, type, value=None): - self.type = type + def __init__(self, type_, value=None): + self.type = type_ if value is not None: self.value = value else: - self.value = type.default_atom() + self.value = type_.default_atom() def __cmp__(self, other): if not isinstance(other, Atom) or self.type != other.type: @@ -85,8 +80,20 @@ class Atom(object): return hash(self.value) @staticmethod - def default(type): - return Atom(type) + 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): return self == self.default(self.type) @@ -101,18 +108,27 @@ class Atom(object): 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.UUID.from_json(json, symtab)) + atom = Atom(type_, ovs.ovsuuid.from_json(json, symtab)) else: raise error.Error("expected %s" % type_.to_string(), json) 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 @@ -143,16 +159,16 @@ class Atom(object): length = len(s) if length < base.min_length: raise ConstraintViolation( - "\"%s\" length %d is less than minimum allowed length %d" + '"%s" length %d is less than minimum allowed length %d' % (s, length, base.min_length)) elif length > base.max_length: raise ConstraintViolation( - "\"%s\" length %d is greater than maximum allowed " - "length %d" % (s, length, base.max_length)) + '"%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 self.value.to_json() + return ovs.ovsuuid.to_json(self.value) else: return self.value @@ -170,7 +186,7 @@ class Atom(object): return ['%s.string = xstrdup("%s");' % (var, escapeCString(self.value))] elif self.type == ovs.db.types.UuidType: - return self.value.cInitUUID(var) + return ovs.ovsuuid.to_c_assignment(self.value, var) def toEnglish(self, escapeLiteral=returnUnchanged): if self.type == ovs.db.types.IntegerType: @@ -217,7 +233,7 @@ class Atom(object): elif type(x) == float: t = ovs.db.types.RealType elif x in [False, True]: - t = ovs.db.types.RealType + t = ovs.db.types.BooleanType elif type(x) in [str, unicode]: t = ovs.db.types.StringType elif isinstance(x, uuid): @@ -227,8 +243,8 @@ class Atom(object): return Atom(t, x) class Datum(object): - def __init__(self, type, values={}): - self.type = type + def __init__(self, type_, values={}): + self.type = type_ self.values = values def __cmp__(self, other): @@ -246,18 +262,18 @@ class Datum(object): def __contains__(self, item): return item in self.values - def clone(self): + def copy(self): return Datum(self.type, dict(self.values)) @staticmethod - def default(type): - if type.n_min == 0: + def default(type_): + if type_.n_min == 0: values = {} - elif type.is_map(): - values = {type.key.default(): type.value.default()} + elif type_.is_map(): + values = {type_.key.default(): type_.value.default()} else: - values = {type.key.default(): None} - return Datum(type, values) + values = {type_.key.default(): None} + return Datum(type_, values) def is_default(self): return self == Datum.default(self.type) @@ -269,10 +285,10 @@ class Datum(object): This function is not commonly useful because the most ordinary way to obtain a datum is ultimately via Datum.from_json() or Atom.from_json(), which check constraints themselves.""" - for keyAtom, valueAtom in self.values: - keyAtom.check_constraints() + for keyAtom, valueAtom in self.values.iteritems(): + keyAtom.check_constraints(self.type.key) if valueAtom is not None: - valueAtom.check_constraints() + valueAtom.check_constraints(self.type.value) @staticmethod def from_json(type_, json, symtab=None): @@ -293,7 +309,8 @@ class Datum(object): else: class_ = "set" - inner = ovs.db.parser.unwrap_json(json, class_, list) + inner = ovs.db.parser.unwrap_json(json, class_, [list, tuple], + "array") n = len(inner) if n < type_.n_min or n > type_.n_max: raise error.Error("%s must have %d to %d members but %d are " @@ -325,41 +342,41 @@ class Datum(object): return Datum(type_, {keyAtom: None}) def to_json(self): - if len(self.values) == 1 and not self.type.is_map(): + if self.type.is_map(): + return ["map", [[k.to_json(), v.to_json()] + for k, v in sorted(self.values.items())]] + elif len(self.values) == 1: key = self.values.keys()[0] return key.to_json() - elif not self.type.is_map(): - return ["set", [k.to_json() for k in sorted(self.values.keys())]] else: - return ["map", [[k.to_json(), v.to_json()] - for k, v in sorted(self.values.items())]] + return ["set", [k.to_json() for k in sorted(self.values.keys())]] def to_string(self): + head = tail = None if self.type.n_max > 1 or len(self.values) == 0: if self.type.is_map(): - s = "{" + head = "{" + tail = "}" else: - s = "[" - else: - s = "" + head = "[" + tail = "]" - i = 0 - for key in sorted(self.values): - if i > 0: - s += ", " - i += 1 + s = [] + if head: + s.append(head) - if self.type.is_map(): - s += "%s=%s" % (key.to_string(), self.values[key].to_string()) - else: - s += key.to_string() + for i, key in enumerate(sorted(self.values)): + if i: + s.append(", ") - if self.type.n_max > 1 or len(self.values) == 0: + s.append(key.to_string()) if self.type.is_map(): - s += "}" - else: - s += "]" - return s + s.append("=") + s.append(self.values[key].to_string()) + + if tail: + s.append(tail) + return ''.join(s) def as_list(self): if self.type.is_map(): @@ -367,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(): @@ -377,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) @@ -400,7 +511,7 @@ class Datum(object): def conforms_to_type(self): n = len(self.values) - return n >= self.type.n_min and n <= self.type.n_max + return self.type.n_min <= n <= self.type.n_max def cInitDatum(self, var): if len(self.values) == 0: @@ -410,18 +521,14 @@ class Datum(object): s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);" % (var, len(self.values), var)] - i = 0 - for key, value in sorted(self.values.items()): + for i, key in enumerate(sorted(self.values)): s += key.cInitAtom("%s->keys[%d]" % (var, i)) - i += 1 if self.type.value: s += ["%s->values = xmalloc(%d * sizeof *%s->values);" % (var, len(self.values), var)] - i = 0 - for key, value in sorted(self.values.items()): + for i, (key, value) in enumerate(sorted(self.values.items())): s += value.cInitAtom("%s->values[%d]" % (var, i)) - i += 1 else: s += ["%s->values = NULL;" % var]