1 # Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
20 from ovs.db import error
26 class AtomicType(object):
27 def __init__(self, name, default, python_types):
29 self.default = default
30 self.python_types = python_types
35 for atomic_type in ATOMIC_TYPES:
36 if s == atomic_type.name:
38 raise error.Error('"%s" is not an atomic-type' % s, s)
42 if not isinstance(json, six.string_types):
43 raise error.Error("atomic-type expected", json)
45 return AtomicType.from_string(json)
56 def default_atom(self):
57 return ovs.db.data.Atom(self, self.default)
59 REAL_PYTHON_TYPES = list(six.integer_types)
60 REAL_PYTHON_TYPES.extend([float])
61 REAL_PYTHON_TYPES = tuple(REAL_PYTHON_TYPES)
63 VoidType = AtomicType("void", None, ())
64 IntegerType = AtomicType("integer", 0, six.integer_types)
65 RealType = AtomicType("real", 0.0, REAL_PYTHON_TYPES)
66 BooleanType = AtomicType("boolean", False, (bool,))
67 StringType = AtomicType("string", "", six.string_types)
68 UuidType = AtomicType("uuid", ovs.ovsuuid.zero(), (uuid.UUID,))
70 ATOMIC_TYPES = [VoidType, IntegerType, RealType, BooleanType, StringType,
74 def escapeCString(src):
95 dst += '\\%03o' % ord(c)
102 """Returns integer x formatted in decimal with thousands set off by
104 return _commafy("%d" % x)
108 if s.startswith('-'):
109 return '-' + _commafy(s[1:])
113 return _commafy(s[:-3]) + ',' + _commafy(s[-3:])
116 def returnUnchanged(x):
120 class BaseType(object):
121 def __init__(self, type_, enum=None, min=None, max=None,
122 min_length=0, max_length=sys.maxint, ref_table_name=None):
123 assert isinstance(type_, AtomicType)
128 self.min_length = min_length
129 self.max_length = max_length
130 self.ref_table_name = ref_table_name
132 self.ref_type = 'strong'
135 self.ref_table = None
138 return ovs.db.data.Atom.default(self.type)
140 def __eq__(self, other):
141 if not isinstance(other, BaseType):
142 return NotImplemented
143 return (self.type == other.type and self.enum == other.enum and
144 self.min == other.min and self.max == other.max and
145 self.min_length == other.min_length and
146 self.max_length == other.max_length and
147 self.ref_table_name == other.ref_table_name)
149 def __ne__(self, other):
150 if not isinstance(other, BaseType):
151 return NotImplemented
153 return not (self == other)
156 def __parse_uint(parser, name, default):
157 value = parser.get_optional(name, six.integer_types)
161 max_value = 2 ** 32 - 1
162 if not (0 <= value <= max_value):
163 raise error.Error("%s out of valid range 0 to %d"
164 % (name, max_value), value)
169 if isinstance(json, six.string_types):
170 return BaseType(AtomicType.from_json(json))
172 parser = ovs.db.parser.Parser(json, "ovsdb type")
173 atomic_type = AtomicType.from_json(parser.get("type",
176 base = BaseType(atomic_type)
178 enum = parser.get_optional("enum", [])
180 base.enum = ovs.db.data.Datum.from_json(
181 BaseType.get_enum_type(base.type), enum)
182 elif base.type == IntegerType:
183 base.min = parser.get_optional("minInteger", six.integer_types)
184 base.max = parser.get_optional("maxInteger", six.integer_types)
185 if (base.min is not None and base.max is not None
186 and base.min > base.max):
187 raise error.Error("minInteger exceeds maxInteger", json)
188 elif base.type == RealType:
189 base.min = parser.get_optional("minReal", REAL_PYTHON_TYPES)
190 base.max = parser.get_optional("maxReal", REAL_PYTHON_TYPES)
191 if (base.min is not None and base.max is not None
192 and base.min > base.max):
193 raise error.Error("minReal exceeds maxReal", json)
194 elif base.type == StringType:
195 base.min_length = BaseType.__parse_uint(parser, "minLength", 0)
196 base.max_length = BaseType.__parse_uint(parser, "maxLength",
198 if base.min_length > base.max_length:
199 raise error.Error("minLength exceeds maxLength", json)
200 elif base.type == UuidType:
201 base.ref_table_name = parser.get_optional("refTable", ['id'])
202 if base.ref_table_name:
203 base.ref_type = parser.get_optional("refType",
206 if base.ref_type not in ['strong', 'weak']:
207 raise error.Error('refType must be "strong" or "weak" '
208 '(not "%s")' % base.ref_type)
214 if not self.has_constraints():
215 return self.type.to_json()
217 json = {'type': self.type.to_json()}
220 json['enum'] = self.enum.to_json()
222 if self.type == IntegerType:
223 if self.min is not None:
224 json['minInteger'] = self.min
225 if self.max is not None:
226 json['maxInteger'] = self.max
227 elif self.type == RealType:
228 if self.min is not None:
229 json['minReal'] = self.min
230 if self.max is not None:
231 json['maxReal'] = self.max
232 elif self.type == StringType:
233 if self.min_length != 0:
234 json['minLength'] = self.min_length
235 if self.max_length != sys.maxint:
236 json['maxLength'] = self.max_length
237 elif self.type == UuidType:
238 if self.ref_table_name:
239 json['refTable'] = self.ref_table_name
240 if self.ref_type != 'strong':
241 json['refType'] = self.ref_type
245 base = BaseType(self.type, self.enum.copy(), self.min, self.max,
246 self.min_length, self.max_length, self.ref_table_name)
247 base.ref_table = self.ref_table
251 if self.type in (VoidType, BooleanType, UuidType):
253 elif self.type in (IntegerType, RealType):
254 return self.min is None or self.max is None or self.min <= self.max
255 elif self.type == StringType:
256 return self.min_length <= self.max_length
260 def has_constraints(self):
261 return (self.enum is not None or self.min is not None or
262 self.max is not None or
263 self.min_length != 0 or self.max_length != sys.maxint or
264 self.ref_table_name is not None)
266 def without_constraints(self):
267 return BaseType(self.type)
270 def get_enum_type(atomic_type):
271 """Returns the type of the 'enum' member for a BaseType whose
272 'type' is 'atomic_type'."""
273 return Type(BaseType(atomic_type), None, 1, sys.maxint)
276 return self.type == UuidType and self.ref_table_name is not None
278 def is_strong_ref(self):
279 return self.is_ref() and self.ref_type == 'strong'
281 def is_weak_ref(self):
282 return self.is_ref() and self.ref_type == 'weak'
284 def toEnglish(self, escapeLiteral=returnUnchanged):
285 if self.type == UuidType and self.ref_table_name:
286 s = escapeLiteral(self.ref_table_name)
287 if self.ref_type == 'weak':
288 s = "weak reference to " + s
291 return self.type.to_string()
293 def constraintsToEnglish(self, escapeLiteral=returnUnchanged,
294 escapeNumber=returnUnchanged):
296 literals = [value.toEnglish(escapeLiteral)
297 for value in self.enum.values]
298 if len(literals) == 1:
299 english = 'must be %s' % (literals[0])
300 elif len(literals) == 2:
301 english = 'either %s or %s' % (literals[0], literals[1])
303 english = 'one of %s, %s, or %s' % (literals[0],
304 ', '.join(literals[1:-1]),
306 elif self.min is not None and self.max is not None:
307 if self.type == IntegerType:
308 english = 'in range %s to %s' % (
309 escapeNumber(commafy(self.min)),
310 escapeNumber(commafy(self.max)))
312 english = 'in range %s to %s' % (
313 escapeNumber("%g" % self.min),
314 escapeNumber("%g" % self.max))
315 elif self.min is not None:
316 if self.type == IntegerType:
317 english = 'at least %s' % escapeNumber(commafy(self.min))
319 english = 'at least %s' % escapeNumber("%g" % self.min)
320 elif self.max is not None:
321 if self.type == IntegerType:
322 english = 'at most %s' % escapeNumber(commafy(self.max))
324 english = 'at most %s' % escapeNumber("%g" % self.max)
325 elif self.min_length != 0 and self.max_length != sys.maxint:
326 if self.min_length == self.max_length:
327 english = ('exactly %s characters long'
328 % commafy(self.min_length))
330 english = ('between %s and %s characters long'
331 % (commafy(self.min_length),
332 commafy(self.max_length)))
333 elif self.min_length != 0:
334 return 'at least %s characters long' % commafy(self.min_length)
335 elif self.max_length != sys.maxint:
336 english = 'at most %s characters long' % commafy(self.max_length)
342 def toCType(self, prefix):
343 if self.ref_table_name:
344 return "struct %s%s *" % (prefix, self.ref_table_name.lower())
346 return {IntegerType: 'int64_t ',
348 UuidType: 'struct uuid ',
349 BooleanType: 'bool ',
350 StringType: 'char *'}[self.type]
352 def toAtomicType(self):
353 return "OVSDB_TYPE_%s" % self.type.to_string().upper()
355 def copyCValue(self, dst, src):
356 args = {'dst': dst, 'src': src}
357 if self.ref_table_name:
358 return ("%(dst)s = %(src)s->header_.uuid;") % args
359 elif self.type == StringType:
360 return "%(dst)s = xstrdup(%(src)s);" % args
362 return "%(dst)s = %(src)s;" % args
364 def assign_c_value_casting_away_const(self, dst, src):
365 args = {'dst': dst, 'src': src}
366 if self.ref_table_name:
367 return ("%(dst)s = %(src)s->header_.uuid;") % args
368 elif self.type == StringType:
369 return "%(dst)s = CONST_CAST(char *, %(src)s);" % args
371 return "%(dst)s = %(src)s;" % args
373 def initCDefault(self, var, is_optional):
374 if self.ref_table_name:
375 return "%s = NULL;" % var
376 elif self.type == StringType and not is_optional:
377 return '%s = "";' % var
379 pattern = {IntegerType: '%s = 0;',
380 RealType: '%s = 0.0;',
381 UuidType: 'uuid_zero(&%s);',
382 BooleanType: '%s = false;',
383 StringType: '%s = NULL;'}[self.type]
386 def cInitBaseType(self, indent, var):
388 stmts.append('ovsdb_base_type_init(&%s, %s);' % (
389 var, self.toAtomicType()))
391 stmts.append("%s.enum_ = xmalloc(sizeof *%s.enum_);"
393 stmts += self.enum.cInitDatum("%s.enum_" % var)
394 if self.type == IntegerType:
395 if self.min is not None:
396 stmts.append('%s.u.integer.min = INT64_C(%d);'
398 if self.max is not None:
399 stmts.append('%s.u.integer.max = INT64_C(%d);'
401 elif self.type == RealType:
402 if self.min is not None:
403 stmts.append('%s.u.real.min = %d;' % (var, self.min))
404 if self.max is not None:
405 stmts.append('%s.u.real.max = %d;' % (var, self.max))
406 elif self.type == StringType:
407 if self.min_length is not None:
408 stmts.append('%s.u.string.minLen = %d;'
409 % (var, self.min_length))
410 if self.max_length != sys.maxint:
411 stmts.append('%s.u.string.maxLen = %d;'
412 % (var, self.max_length))
413 elif self.type == UuidType:
414 if self.ref_table_name is not None:
415 stmts.append('%s.u.uuid.refTableName = "%s";'
416 % (var, escapeCString(self.ref_table_name)))
417 stmts.append('%s.u.uuid.refType = OVSDB_REF_%s;'
418 % (var, self.ref_type.upper()))
419 return '\n'.join([indent + stmt for stmt in stmts])
426 def __init__(self, key, value=None, n_min=DEFAULT_MIN, n_max=DEFAULT_MAX):
433 if self.value is None:
436 value = self.value.copy()
437 return Type(self.key.copy(), value, self.n_min, self.n_max)
439 def __eq__(self, other):
440 if not isinstance(other, Type):
441 return NotImplemented
442 return (self.key == other.key and self.value == other.value and
443 self.n_min == other.n_min and self.n_max == other.n_max)
445 def __ne__(self, other):
446 if not isinstance(other, Type):
447 return NotImplemented
449 return not (self == other)
452 return (self.key.type != VoidType and self.key.is_valid() and
453 (self.value is None or
454 (self.value.type != VoidType and self.value.is_valid())) and
455 self.n_min <= 1 <= self.n_max)
458 return self.n_min == 1 and self.n_max == 1 and not self.value
460 def is_optional(self):
461 return self.n_min == 0 and self.n_max == 1
463 def is_composite(self):
464 return self.n_max > 1
467 return self.value is None and (self.n_min != 1 or self.n_max != 1)
470 return self.value is not None
473 return (self.is_map()
474 and self.key.type == StringType
475 and self.value.type == StringType)
477 def is_optional_pointer(self):
478 return (self.is_optional() and not self.value
479 and (self.key.type == StringType or self.key.ref_table_name))
482 def __n_from_json(json, default):
485 elif type(json) == int and 0 <= json <= sys.maxint:
488 raise error.Error("bad min or max value", json)
492 if isinstance(json, six.string_types):
493 return Type(BaseType.from_json(json))
495 parser = ovs.db.parser.Parser(json, "ovsdb type")
496 _types = list(six.string_types)
497 _types.extend([dict])
498 key_json = parser.get("key", _types)
499 value_json = parser.get_optional("value", _types)
500 min_json = parser.get_optional("min", [int])
501 _types = list(six.string_types)
503 max_json = parser.get_optional("max", _types)
506 key = BaseType.from_json(key_json)
508 value = BaseType.from_json(value_json)
512 n_min = Type.__n_from_json(min_json, Type.DEFAULT_MIN)
514 if max_json == 'unlimited':
517 n_max = Type.__n_from_json(max_json, Type.DEFAULT_MAX)
519 type_ = Type(key, value, n_min, n_max)
520 if not type_.is_valid():
521 raise error.Error("ovsdb type fails constraint checks", json)
525 if self.is_scalar() and not self.key.has_constraints():
526 return self.key.to_json()
528 json = {"key": self.key.to_json()}
529 if self.value is not None:
530 json["value"] = self.value.to_json()
531 if self.n_min != Type.DEFAULT_MIN:
532 json["min"] = self.n_min
533 if self.n_max == sys.maxint:
534 json["max"] = "unlimited"
535 elif self.n_max != Type.DEFAULT_MAX:
536 json["max"] = self.n_max
539 def toEnglish(self, escapeLiteral=returnUnchanged):
540 keyName = self.key.toEnglish(escapeLiteral)
542 valueName = self.value.toEnglish(escapeLiteral)
546 elif self.is_optional():
548 return "optional %s-%s pair" % (keyName, valueName)
550 return "optional %s" % keyName
552 if self.n_max == sys.maxint:
554 quantity = "%s or more " % commafy(self.n_min)
558 quantity = "%s to %s " % (commafy(self.n_min),
561 quantity = "up to %s " % commafy(self.n_max)
564 return "map of %s%s-%s pairs" % (quantity, keyName, valueName)
566 if keyName.endswith('s'):
567 plural = keyName + "es"
569 plural = keyName + "s"
570 return "set of %s%s" % (quantity, plural)
572 def constraintsToEnglish(self, escapeLiteral=returnUnchanged,
573 escapeNumber=returnUnchanged):
575 keyConstraints = self.key.constraintsToEnglish(escapeLiteral,
579 constraints.append('key %s' % keyConstraints)
581 constraints.append(keyConstraints)
584 valueConstraints = self.value.constraintsToEnglish(escapeLiteral,
587 constraints.append('value %s' % valueConstraints)
589 return ', '.join(constraints)
591 def cDeclComment(self):
592 if self.n_min == 1 and self.n_max == 1 and self.key.type == StringType:
593 return "\t/* Always nonnull. */"
597 def cInitType(self, indent, var):
598 initKey = self.key.cInitBaseType(indent, "%s.key" % var)
600 initValue = self.value.cInitBaseType(indent, "%s.value" % var)
602 initValue = ('%sovsdb_base_type_init(&%s.value, '
603 'OVSDB_TYPE_VOID);' % (indent, var))
604 initMin = "%s%s.n_min = %s;" % (indent, var, self.n_min)
605 if self.n_max == sys.maxint:
609 initMax = "%s%s.n_max = %s;" % (indent, var, n_max)
610 return "\n".join((initKey, initValue, initMin, initMax))