-class Error(Exception):
- def __init__(self, msg):
- Exception.__init__(self)
- self.msg = msg
-
-def getMember(json, name, validTypes, description, default=None):
- if name in json:
- member = json[name]
- if len(validTypes) and type(member) not in validTypes:
- raise Error("%s: type mismatch for '%s' member"
- % (description, name))
- return member
- return default
-
-def mustGetMember(json, name, expectedType, description):
- member = getMember(json, name, expectedType, description)
- if member == None:
- raise Error("%s: missing '%s' member" % (description, name))
- return member
-
-class DbSchema:
- def __init__(self, name, comment, tables, idlPrefix, idlHeader):
- self.name = name
- self.comment = comment
- self.tables = tables
- self.idlPrefix = idlPrefix
- self.idlHeader = idlHeader
-
- @staticmethod
- def fromJson(json):
- name = mustGetMember(json, 'name', [unicode], 'database')
- comment = getMember(json, 'comment', [unicode], 'database')
- tablesJson = mustGetMember(json, 'tables', [dict], 'database')
- tables = {}
- for tableName, tableJson in tablesJson.iteritems():
- tables[tableName] = TableSchema.fromJson(tableJson,
- "%s table" % tableName)
- idlPrefix = mustGetMember(json, 'idlPrefix', [unicode], 'database')
- idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database')
- return DbSchema(name, comment, tables, idlPrefix, idlHeader)
-
-class TableSchema:
- def __init__(self, comment, columns):
- self.comment = comment
- self.columns = columns
-
- @staticmethod
- def fromJson(json, description):
- comment = getMember(json, 'comment', [unicode], description)
- columnsJson = mustGetMember(json, 'columns', [dict], description)
- columns = {}
- for name, json in columnsJson.iteritems():
- columns[name] = ColumnSchema.fromJson(
- json, "column %s in %s" % (name, description))
- return TableSchema(comment, columns)
-
-class ColumnSchema:
- def __init__(self, comment, type, persistent):
- self.comment = comment
- self.type = type
- self.persistent = persistent
-
- @staticmethod
- def fromJson(json, description):
- comment = getMember(json, 'comment', [unicode], description)
- type = Type.fromJson(mustGetMember(json, 'type', [dict, unicode],
- description),
- 'type of %s' % description)
- ephemeral = getMember(json, 'ephemeral', [bool], description)
- persistent = ephemeral != True
- return ColumnSchema(comment, type, persistent)
-
-def escapeCString(src):
- dst = ""
- for c in src:
- if c in "\\\"":
- dst += "\\" + c
- elif ord(c) < 32:
- if c == '\n':
- dst += '\\n'
- elif c == '\r':
- dst += '\\r'
- elif c == '\a':
- dst += '\\a'
- elif c == '\b':
- dst += '\\b'
- elif c == '\f':
- dst += '\\f'
- elif c == '\t':
- dst += '\\t'
- elif c == '\v':
- dst += '\\v'
- else:
- dst += '\\%03o' % ord(c)
- else:
- dst += c
- return dst
-
-class UUID:
- x = "[0-9a-fA-f]"
- uuidRE = re.compile("^(%s{8})-(%s{4})-(%s{4})-(%s{4})-(%s{4})(%s{8})$"
- % (x, x, x, x, x, x))
-
- def __init__(self, value):
- self.value = value
-
- @staticmethod
- def fromString(s):
- if not uuidRE.match(s):
- raise Error("%s is not a valid UUID" % s)
- return UUID(s)
-
- @staticmethod
- def fromJson(json):
- if UUID.isValidJson(json):
- return UUID(json[1])
- else:
- raise Error("%s is not valid JSON for a UUID" % json)
-
- @staticmethod
- def isValidJson(json):
- return len(json) == 2 and json[0] == "uuid" and uuidRE.match(json[1])
-
- def toJson(self):
- return ["uuid", self.value]
-
- def cInitUUID(self, var):
- m = re.match(self.value)
- return ["%s.parts[0] = 0x%s;" % (var, m.group(1)),
- "%s.parts[1] = 0x%s%s;" % (var, m.group(2), m.group(3)),
- "%s.parts[2] = 0x%s%s;" % (var, m.group(4), m.group(5)),
- "%s.parts[3] = 0x%s;" % (var, m.group(6))]
-
-class Atom:
- def __init__(self, type, value):
- self.type = type
- self.value = value
-
- @staticmethod
- def fromJson(type_, json):
- if ((type_ == 'integer' and type(json) in [int, long])
- or (type_ == 'real' and type(json) in [int, long, float])
- or (type_ == 'boolean' and json in [True, False])
- or (type_ == 'string' and type(json) in [str, unicode])):
- return Atom(type_, json)
- elif type_ == 'uuid':
- return UUID.fromJson(json)
- else:
- raise Error("%s is not valid JSON for type %s" % (json, type_))
-
- def toJson(self):
- if self.type == 'uuid':
- return self.value.toString()
- else:
- return self.value
-
- def cInitAtom(self, var):
- if self.type == 'integer':
- return ['%s.integer = %d;' % (var, self.value)]
- elif self.type == 'real':
- return ['%s.real = %.15g;' % (var, self.value)]
- elif self.type == 'boolean':
- if self.value:
- return ['%s.boolean = true;']
- else:
- return ['%s.boolean = false;']
- elif self.type == 'string':
- return ['%s.string = xstrdup("%s");'
- % (var, escapeCString(self.value))]
- elif self.type == 'uuid':
- return self.value.cInitUUID(var)
-
-class BaseType:
- def __init__(self, type,
- enum=None,
- refTable=None,
- minInteger=None, maxInteger=None,
- minReal=None, maxReal=None,
- minLength=None, maxLength=None):
- self.type = type
- self.enum = enum
- self.refTable = refTable
- self.minInteger = minInteger
- self.maxInteger = maxInteger
- self.minReal = minReal
- self.maxReal = maxReal
- self.minLength = minLength
- self.maxLength = maxLength
-
- @staticmethod
- def fromJson(json, description):
- if type(json) == unicode:
- return BaseType(json)
- else:
- atomicType = mustGetMember(json, 'type', [unicode], description)
- enum = getMember(json, 'enum', [], description)
- if enum:
- enumType = Type(atomicType, None, 0, 'unlimited')
- enum = Datum.fromJson(enumType, enum)
- refTable = getMember(json, 'refTable', [unicode], description)
- minInteger = getMember(json, 'minInteger', [int, long], description)
- maxInteger = getMember(json, 'maxInteger', [int, long], description)
- minReal = getMember(json, 'minReal', [int, long, float], description)
- maxReal = getMember(json, 'maxReal', [int, long, float], description)
- minLength = getMember(json, 'minLength', [int], description)
- maxLength = getMember(json, 'minLength', [int], description)
- return BaseType(atomicType, enum, refTable, minInteger, maxInteger, minReal, maxReal, minLength, maxLength)
-
- def toEnglish(self):
- if self.type == 'uuid' and self.refTable:
- return self.refTable
- else:
- return self.type
-
- def toCType(self, prefix):
- if self.refTable:
- return "struct %s%s *" % (prefix, self.refTable.lower())
- else:
- return {'integer': 'int64_t ',
- 'real': 'double ',
- 'uuid': 'struct uuid ',
- 'boolean': 'bool ',
- 'string': 'char *'}[self.type]
-
- def copyCValue(self, dst, src):
- args = {'dst': dst, 'src': src}
- if self.refTable:
- return ("%(dst)s = %(src)s->header_.uuid;") % args
- elif self.type == 'string':
- return "%(dst)s = xstrdup(%(src)s);" % args
- else:
- return "%(dst)s = %(src)s;" % args
-
- def initCDefault(self, var, isOptional):
- if self.refTable:
- return "%s = NULL;" % var
- elif self.type == 'string' and not isOptional:
- return "%s = \"\";" % var
- else:
- return {'integer': '%s = 0;',
- 'real': '%s = 0.0;',
- 'uuid': 'uuid_zero(&%s);',
- 'boolean': '%s = false;',
- 'string': '%s = NULL;'}[self.type] % var
-
- def cInitBaseType(self, indent, var):
- stmts = []
- stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % (
- var, self.type.upper()),)
- if self.enum:
- stmts.append("%s.enum_ = xmalloc(sizeof *%s.enum_);"
- % (var, var))
- stmts += self.enum.cInitDatum("%s.enum_" % var)
- if self.type == 'integer':
- if self.minInteger != None:
- stmts.append('%s.u.integer.min = %d;' % (var, self.minInteger))
- if self.maxInteger != None:
- stmts.append('%s.u.integer.max = %d;' % (var, self.maxInteger))
- elif self.type == 'real':
- if self.minReal != None:
- stmts.append('%s.u.real.min = %d;' % (var, self.minReal))
- if self.maxReal != None:
- stmts.append('%s.u.real.max = %d;' % (var, self.maxReal))
- elif self.type == 'string':
- if self.minLength != None:
- stmts.append('%s.u.string.minLen = %d;' % (var, self.minLength))
- if self.maxLength != None:
- stmts.append('%s.u.string.maxLen = %d;' % (var, self.maxLength))
- elif self.type == 'uuid':
- if self.refTable != None:
- stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.refTable)))
- return '\n'.join([indent + stmt for stmt in stmts])
-
-class Type:
- def __init__(self, key, value=None, min=1, max=1):
- self.key = key
- self.value = value
- self.min = min
- self.max = max
-
- @staticmethod
- def fromJson(json, description):
- if type(json) == unicode:
- return Type(BaseType(json))
- else:
- keyJson = mustGetMember(json, 'key', [dict, unicode], description)
- key = BaseType.fromJson(keyJson, 'key in %s' % description)
-
- valueJson = getMember(json, 'value', [dict, unicode], description)
- if valueJson:
- value = BaseType.fromJson(valueJson,
- 'value in %s' % description)
- else:
- value = None
-
- min = getMember(json, 'min', [int], description, 1)
- max = getMember(json, 'max', [int, unicode], description, 1)
- return Type(key, value, min, max)
-
- def isScalar(self):
- return self.min == 1 and self.max == 1 and not self.value
-
- def isOptional(self):
- return self.min == 0 and self.max == 1
-
- def isOptionalPointer(self):
- return (self.min == 0 and self.max == 1 and not self.value
- and (self.key.type == 'string' or self.key.refTable))
-
- def toEnglish(self):
- keyName = self.key.toEnglish()
- if self.value:
- valueName = self.value.toEnglish()
-
- if self.isScalar():
- return keyName
- elif self.isOptional():
- if self.value:
- return "optional %s-%s pair" % (keyName, valueName)
- else:
- return "optional %s" % keyName
- else:
- if self.max == "unlimited":
- if self.min:
- quantity = "%d or more " % self.min
- else:
- quantity = ""
- elif self.min:
- quantity = "%d to %d " % (self.min, self.max)
- else:
- quantity = "up to %d " % self.max
-
- if self.value:
- return "map of %s%s-%s pairs" % (quantity, keyName, valueName)
- else:
- return "set of %s%s" % (quantity, keyName)
-
- def cDeclComment(self):
- if self.min == 1 and self.max == 1 and self.key.type == "string":
- return "\t/* Always nonnull. */"
- else:
- return ""
-
- def cInitType(self, indent, var):
- initKey = self.key.cInitBaseType(indent, "%s.key" % var)
- if self.value:
- initValue = self.value.cInitBaseType(indent, "%s.value" % var)
- else:
- initValue = ('%sovsdb_base_type_init(&%s.value, '
- 'OVSDB_TYPE_VOID);' % (indent, var))
- initMin = "%s%s.n_min = %s;" % (indent, var, self.min)
- if self.max == "unlimited":
- max = "UINT_MAX"
- else:
- max = self.max
- initMax = "%s%s.n_max = %s;" % (indent, var, max)
- return "\n".join((initKey, initValue, initMin, initMax))
-
-class Datum:
- def __init__(self, type, values):
- self.type = type
- self.values = values
-
- @staticmethod
- def fromJson(type_, json):
- if not type_.value:
- if len(json) == 2 and json[0] == "set":
- values = []
- for atomJson in json[1]:
- values += [Atom.fromJson(type_.key, atomJson)]
- else:
- values = [Atom.fromJson(type_.key, json)]
- else:
- if len(json) != 2 or json[0] != "map":
- raise Error("%s is not valid JSON for a map" % json)
- values = []
- for pairJson in json[1]:
- values += [(Atom.fromJson(type_.key, pairJson[0]),
- Atom.fromJson(type_.value, pairJson[1]))]
- return Datum(type_, values)
-
- def cInitDatum(self, var):
- if len(self.values) == 0:
- return ["ovsdb_datum_init_empty(%s);" % var]
-
- s = ["%s->n = %d;" % (var, len(self.values))]
- s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);"
- % (var, len(self.values), var)]
-
- for i in range(len(self.values)):
- key = self.values[i]
- if self.type.value:
- key = key[0]
- 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)]
- for i in range(len(self.values)):
- value = self.values[i][1]
- s += key.cInitAtom("%s->values[%d]" % (var, i))
- else:
- s += ["%s->values = NULL;" % var]
-
- if len(self.values) > 1:
- s += ["ovsdb_datum_sort_assert(%s, OVSDB_TYPE_%s);"
- % (var, self.type.key.upper())]
-
- return s
-