python: Implement write support in Python IDL for OVSDB.
[sliver-openvswitch.git] / python / ovs / db / types.py
index a3b6ba7..6050197 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira Networks
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -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):
@@ -30,16 +32,14 @@ class AtomicType(object):
             for atomic_type in ATOMIC_TYPES:
                 if s == atomic_type.name:
                     return atomic_type
-        raise error.Error("\"%s\" is not an atomic type" % s)
+        raise error.Error('"%s" is not an atomic-type' % s, s)
 
     @staticmethod
     def from_json(json):
         if type(json) not in [str, unicode]:
             raise error.Error("atomic-type expected", json)
-        try:
+        else:
             return AtomicType.from_string(json)
-        except error.Error:
-            raise error.Error("\"%s\" is not an atomic-type" % json, json)
 
     def __str__(self):
         return self.name
@@ -53,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.UUID.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]
@@ -106,7 +106,7 @@ def returnUnchanged(x):
 
 class BaseType(object):
     def __init__(self, type_, enum=None, min=None, max=None,
-                 min_length = 0, max_length=sys.maxint, ref_table=None):
+                 min_length = 0, max_length=sys.maxint, ref_table_name=None):
         assert isinstance(type_, AtomicType)
         self.type = type_
         self.enum = enum
@@ -114,7 +114,12 @@ class BaseType(object):
         self.max = max
         self.min_length = min_length
         self.max_length = max_length
-        self.ref_table = ref_table
+        self.ref_table_name = ref_table_name
+        if ref_table_name:
+            self.ref_type = 'strong'
+        else:
+            self.ref_type = None
+        self.ref_table = None
 
     def default(self):
         return ovs.db.data.Atom.default(self.type)
@@ -126,7 +131,7 @@ class BaseType(object):
                 self.min == other.min and self.max == other.max and
                 self.min_length == other.min_length and
                 self.max_length == other.max_length and
-                self.ref_table == other.ref_table)
+                self.ref_table_name == other.ref_table_name)
 
     def __ne__(self, other):
         if not isinstance(other, BaseType):
@@ -141,14 +146,14 @@ class BaseType(object):
             value = default
         else:
             max_value = 2**32 - 1
-            if value < 0 or value > max_value:
+            if not (0 <= value <= max_value):
                 raise error.Error("%s out of valid range 0 to %d"
                                   % (name, max_value), value)
         return value
 
     @staticmethod
     def from_json(json):
-        if type(json) == unicode:
+        if type(json) in [str, unicode]:
             return BaseType(AtomicType.from_json(json))
 
         parser = ovs.db.parser.Parser(json, "ovsdb type")
@@ -176,13 +181,13 @@ class BaseType(object):
             if base.min_length > base.max_length:
                 raise error.Error("minLength exceeds maxLength", json)
         elif base.type == UuidType:
-            base.ref_table = parser.get_optional("refTable", ['id'])
-            if base.ref_table:
+            base.ref_table_name = parser.get_optional("refTable", ['id'])
+            if base.ref_table_name:
                 base.ref_type = parser.get_optional("refType", [str, unicode],
                                                    "strong")
                 if base.ref_type not in ['strong', 'weak']:
-                    raise error.Error("refType must be \"strong\" or \"weak\" "
-                                      "(not \"%s\")" % base.ref_type)
+                    raise error.Error('refType must be "strong" or "weak" '
+                                      '(not "%s")' % base.ref_type)
         parser.finish()
 
         return base
@@ -212,15 +217,17 @@ class BaseType(object):
             if self.max_length != sys.maxint:
                 json['maxLength'] = self.max_length
         elif self.type == UuidType:
-            if self.ref_table:
-                json['refTable'] = self.ref_table
+            if self.ref_table_name:
+                json['refTable'] = self.ref_table_name
                 if self.ref_type != 'strong':
                     json['refType'] = self.ref_type
         return json
 
-    def clone(self):
-        return BaseType(self.type, self.enum.clone(), self.min, self.max,
-                        self.min_length, self.max_length, self.ref_table)
+    def copy(self):
+        base = BaseType(self.type, self.enum.copy(), self.min, self.max,
+                        self.min_length, self.max_length, self.ref_table_name)
+        base.ref_table = self.ref_table
+        return base
 
     def is_valid(self):
         if self.type in (VoidType, BooleanType, UuidType):
@@ -235,7 +242,7 @@ class BaseType(object):
     def has_constraints(self):
         return (self.enum is not None or self.min is not None or self.max is not None or
                 self.min_length != 0 or self.max_length != sys.maxint or
-                self.ref_table is not None)
+                self.ref_table_name is not None)
 
     def without_constraints(self):
         return BaseType(self.type)
@@ -247,7 +254,7 @@ class BaseType(object):
         return Type(BaseType(atomic_type), None, 1, sys.maxint)
     
     def is_ref(self):
-        return self.type == UuidType and self.ref_table is not None
+        return self.type == UuidType and self.ref_table_name is not None
 
     def is_strong_ref(self):
         return self.is_ref() and self.ref_type == 'strong'
@@ -256,8 +263,8 @@ class BaseType(object):
         return self.is_ref() and self.ref_type == 'weak'
 
     def toEnglish(self, escapeLiteral=returnUnchanged):
-        if self.type == UuidType and self.ref_table:
-            s = escapeLiteral(self.ref_table)
+        if self.type == UuidType and self.ref_table_name:
+            s = escapeLiteral(self.ref_table_name)
             if self.ref_type == 'weak':
                 s = "weak reference to " + s
             return s
@@ -303,8 +310,8 @@ class BaseType(object):
             return ''
 
     def toCType(self, prefix):
-        if self.ref_table:
-            return "struct %s%s *" % (prefix, self.ref_table.lower())
+        if self.ref_table_name:
+            return "struct %s%s *" % (prefix, self.ref_table_name.lower())
         else:
             return {IntegerType: 'int64_t ',
                     RealType: 'double ',
@@ -317,7 +324,7 @@ class BaseType(object):
 
     def copyCValue(self, dst, src):
         args = {'dst': dst, 'src': src}
-        if self.ref_table:
+        if self.ref_table_name:
             return ("%(dst)s = %(src)s->header_.uuid;") % args
         elif self.type == StringType:
             return "%(dst)s = xstrdup(%(src)s);" % args
@@ -325,10 +332,10 @@ class BaseType(object):
             return "%(dst)s = %(src)s;" % args
 
     def initCDefault(self, var, is_optional):
-        if self.ref_table:
+        if self.ref_table_name:
             return "%s = NULL;" % var
         elif self.type == StringType and not is_optional:
-            return "%s = \"\";" % var
+            return '%s = "";' % var
         else:
             pattern = {IntegerType: '%s = 0;',
                        RealType: '%s = 0.0;',
@@ -339,8 +346,8 @@ class BaseType(object):
             
     def cInitBaseType(self, indent, var):
         stmts = []
-        stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % (
-                var, self.type.to_string().upper()),)
+        stmts.append('ovsdb_base_type_init(&%s, %s);' % (
+                var, self.toAtomicType()))
         if self.enum:
             stmts.append("%s.enum_ = xmalloc(sizeof *%s.enum_);"
                          % (var, var))
@@ -361,23 +368,27 @@ class BaseType(object):
             if self.max_length != sys.maxint:
                 stmts.append('%s.u.string.maxLen = %d;' % (var, self.max_length))
         elif self.type == UuidType:
-            if self.ref_table is not None:
-                stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.ref_table)))
+            if self.ref_table_name is not None:
+                stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.ref_table_name)))
+                stmts.append('%s.u.uuid.refType = OVSDB_REF_%s;' % (var, self.ref_type.upper()))
         return '\n'.join([indent + stmt for stmt in stmts])
 
 class Type(object):
-    def __init__(self, key, value=None, n_min=1, n_max=1):
+    DEFAULT_MIN = 1
+    DEFAULT_MAX = 1
+
+    def __init__(self, key, value=None, n_min=DEFAULT_MIN, n_max=DEFAULT_MAX):
         self.key = key
         self.value = value
         self.n_min = n_min
         self.n_max = n_max
 
-    def clone(self):
+    def copy(self):
         if self.value is None:
             value = None
         else:
-            value = self.value.clone()
-        return Type(self.key.clone(), value, self.n_min, self.n_max)
+            value = self.value.copy()
+        return Type(self.key.copy(), value, self.n_min, self.n_max)
 
     def __eq__(self, other):
         if not isinstance(other, Type):
@@ -395,9 +406,7 @@ class Type(object):
         return (self.key.type != VoidType and self.key.is_valid() and
                 (self.value is None or
                  (self.value.type != VoidType and self.value.is_valid())) and
-                self.n_min <= 1 and
-                self.n_min <= self.n_max and
-                self.n_max >= 1)
+                self.n_min <= 1 <= self.n_max)
 
     def is_scalar(self):
         return self.n_min == 1 and self.n_max == 1 and not self.value
@@ -416,13 +425,13 @@ class Type(object):
 
     def is_optional_pointer(self):
         return (self.is_optional() and not self.value
-                and (self.key.type == StringType or self.key.ref_table))
+                and (self.key.type == StringType or self.key.ref_table_name))
 
     @staticmethod
     def __n_from_json(json, default):
         if json is None:
             return default
-        elif type(json) == int and json >= 0 and json <= sys.maxint:
+        elif type(json) == int and 0 <= json <= sys.maxint:
             return json
         else:
             raise error.Error("bad min or max value", json)
@@ -433,8 +442,8 @@ class Type(object):
             return Type(BaseType.from_json(json))
 
         parser = ovs.db.parser.Parser(json, "ovsdb type")
-        key_json = parser.get("key", [dict, unicode])
-        value_json = parser.get_optional("value", [dict, unicode])
+        key_json = parser.get("key", [dict, str, unicode])
+        value_json = parser.get_optional("value", [dict, str, unicode])
         min_json = parser.get_optional("min", [int])
         max_json = parser.get_optional("max", [int, str, unicode])
         parser.finish()
@@ -445,12 +454,12 @@ class Type(object):
         else:
             value = None
 
-        n_min = Type.__n_from_json(min_json, 1)
+        n_min = Type.__n_from_json(min_json, Type.DEFAULT_MIN)
 
         if max_json == 'unlimited':
             n_max = sys.maxint
         else:
-            n_max = Type.__n_from_json(max_json, 1)            
+            n_max = Type.__n_from_json(max_json, Type.DEFAULT_MAX)
 
         type_ = Type(key, value, n_min, n_max)
         if not type_.is_valid():
@@ -464,11 +473,11 @@ class Type(object):
         json = {"key": self.key.to_json()}
         if self.value is not None:
             json["value"] = self.value.to_json()
-        if self.n_min != 1:
+        if self.n_min != Type.DEFAULT_MIN:
             json["min"] = self.n_min
         if self.n_max == sys.maxint:
             json["max"] = "unlimited"
-        elif self.n_max != 1:
+        elif self.n_max != Type.DEFAULT_MAX:
             json["max"] = self.n_max
         return json
 
@@ -505,20 +514,18 @@ class Type(object):
                 return "set of %s%s" % (quantity, plural)
 
     def constraintsToEnglish(self, escapeLiteral=returnUnchanged):
-        s = ""
-
         constraints = []
         keyConstraints = self.key.constraintsToEnglish(escapeLiteral)
         if keyConstraints:
             if self.value:
-                constraints += ['key ' + keyConstraints]
+                constraints.append('key %s' % keyConstraints)
             else:
-                constraints += [keyConstraints]
+                constraints.append(keyConstraints)
 
         if self.value:
             valueConstraints = self.value.constraintsToEnglish(escapeLiteral)
             if valueConstraints:
-                constraints += ['value ' + valueConstraints]
+                constraints.append('value %s' % valueConstraints)
 
         return ', '.join(constraints)
                 
@@ -537,9 +544,9 @@ class Type(object):
                          'OVSDB_TYPE_VOID);' % (indent, var))
         initMin = "%s%s.n_min = %s;" % (indent, var, self.n_min)
         if self.n_max == sys.maxint:
-            max = "UINT_MAX"
+            n_max = "UINT_MAX"
         else:
-            max = self.n_max
-        initMax = "%s%s.n_max = %s;" % (indent, var, max)
+            n_max = self.n_max
+        initMax = "%s%s.n_max = %s;" % (indent, var, n_max)
         return "\n".join((initKey, initValue, initMin, initMax))