]>
git.proxmox.com Git - mirror_ovs.git/blob - python/ovs/db/types.py
1 # Copyright (c) 2009, 2010, 2011 Nicira Networks
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.
18 from ovs
.db
import error
24 class AtomicType(object):
25 def __init__(self
, name
, default
, python_types
):
27 self
.default
= default
28 self
.python_types
= python_types
33 for atomic_type
in ATOMIC_TYPES
:
34 if s
== atomic_type
.name
:
36 raise error
.Error('"%s" is not an atomic-type' % s
, s
)
40 if type(json
) not in [str, unicode]:
41 raise error
.Error("atomic-type expected", json
)
43 return AtomicType
.from_string(json
)
54 def default_atom(self
):
55 return ovs
.db
.data
.Atom(self
, self
.default
)
57 VoidType
= AtomicType("void", None, ())
58 IntegerType
= AtomicType("integer", 0, (int, long))
59 RealType
= AtomicType("real", 0.0, (int, long, float))
60 BooleanType
= AtomicType("boolean", False, (bool,))
61 StringType
= AtomicType("string", "", (str, unicode))
62 UuidType
= AtomicType("uuid", ovs
.ovsuuid
.zero(), (uuid
.UUID
,))
64 ATOMIC_TYPES
= [VoidType
, IntegerType
, RealType
, BooleanType
, StringType
,
68 def escapeCString(src
):
89 dst
+= '\\%03o' % ord(c
)
96 """Returns integer x formatted in decimal with thousands set off by
98 return _commafy("%d" % x
)
102 if s
.startswith('-'):
103 return '-' + _commafy(s
[1:])
107 return _commafy(s
[:-3]) + ',' + _commafy(s
[-3:])
110 def returnUnchanged(x
):
114 class BaseType(object):
115 def __init__(self
, type_
, enum
=None, min=None, max=None,
116 min_length
=0, max_length
=sys
.maxint
, ref_table_name
=None):
117 assert isinstance(type_
, AtomicType
)
122 self
.min_length
= min_length
123 self
.max_length
= max_length
124 self
.ref_table_name
= ref_table_name
126 self
.ref_type
= 'strong'
129 self
.ref_table
= None
132 return ovs
.db
.data
.Atom
.default(self
.type)
134 def __eq__(self
, other
):
135 if not isinstance(other
, BaseType
):
136 return NotImplemented
137 return (self
.type == other
.type and self
.enum
== other
.enum
and
138 self
.min == other
.min and self
.max == other
.max and
139 self
.min_length
== other
.min_length
and
140 self
.max_length
== other
.max_length
and
141 self
.ref_table_name
== other
.ref_table_name
)
143 def __ne__(self
, other
):
144 if not isinstance(other
, BaseType
):
145 return NotImplemented
147 return not (self
== other
)
150 def __parse_uint(parser
, name
, default
):
151 value
= parser
.get_optional(name
, [int, long])
155 max_value
= 2 ** 32 - 1
156 if not (0 <= value
<= max_value
):
157 raise error
.Error("%s out of valid range 0 to %d"
158 % (name
, max_value
), value
)
163 if type(json
) in [str, unicode]:
164 return BaseType(AtomicType
.from_json(json
))
166 parser
= ovs
.db
.parser
.Parser(json
, "ovsdb type")
167 atomic_type
= AtomicType
.from_json(parser
.get("type", [str, unicode]))
169 base
= BaseType(atomic_type
)
171 enum
= parser
.get_optional("enum", [])
173 base
.enum
= ovs
.db
.data
.Datum
.from_json(
174 BaseType
.get_enum_type(base
.type), enum
)
175 elif base
.type == IntegerType
:
176 base
.min = parser
.get_optional("minInteger", [int, long])
177 base
.max = parser
.get_optional("maxInteger", [int, long])
178 if (base
.min is not None and base
.max is not None
179 and base
.min > base
.max):
180 raise error
.Error("minInteger exceeds maxInteger", json
)
181 elif base
.type == RealType
:
182 base
.min = parser
.get_optional("minReal", [int, long, float])
183 base
.max = parser
.get_optional("maxReal", [int, long, float])
184 if (base
.min is not None and base
.max is not None
185 and base
.min > base
.max):
186 raise error
.Error("minReal exceeds maxReal", json
)
187 elif base
.type == StringType
:
188 base
.min_length
= BaseType
.__parse
_uint
(parser
, "minLength", 0)
189 base
.max_length
= BaseType
.__parse
_uint
(parser
, "maxLength",
191 if base
.min_length
> base
.max_length
:
192 raise error
.Error("minLength exceeds maxLength", json
)
193 elif base
.type == UuidType
:
194 base
.ref_table_name
= parser
.get_optional("refTable", ['id'])
195 if base
.ref_table_name
:
196 base
.ref_type
= parser
.get_optional("refType", [str, unicode],
198 if base
.ref_type
not in ['strong', 'weak']:
199 raise error
.Error('refType must be "strong" or "weak" '
200 '(not "%s")' % base
.ref_type
)
206 if not self
.has_constraints():
207 return self
.type.to_json()
209 json
= {'type': self
.type.to_json()}
212 json
['enum'] = self
.enum
.to_json()
214 if self
.type == IntegerType
:
215 if self
.min is not None:
216 json
['minInteger'] = self
.min
217 if self
.max is not None:
218 json
['maxInteger'] = self
.max
219 elif self
.type == RealType
:
220 if self
.min is not None:
221 json
['minReal'] = self
.min
222 if self
.max is not None:
223 json
['maxReal'] = self
.max
224 elif self
.type == StringType
:
225 if self
.min_length
!= 0:
226 json
['minLength'] = self
.min_length
227 if self
.max_length
!= sys
.maxint
:
228 json
['maxLength'] = self
.max_length
229 elif self
.type == UuidType
:
230 if self
.ref_table_name
:
231 json
['refTable'] = self
.ref_table_name
232 if self
.ref_type
!= 'strong':
233 json
['refType'] = self
.ref_type
237 base
= BaseType(self
.type, self
.enum
.copy(), self
.min, self
.max,
238 self
.min_length
, self
.max_length
, self
.ref_table_name
)
239 base
.ref_table
= self
.ref_table
243 if self
.type in (VoidType
, BooleanType
, UuidType
):
245 elif self
.type in (IntegerType
, RealType
):
246 return self
.min is None or self
.max is None or self
.min <= self
.max
247 elif self
.type == StringType
:
248 return self
.min_length
<= self
.max_length
252 def has_constraints(self
):
253 return (self
.enum
is not None or self
.min is not None or
254 self
.max is not None or
255 self
.min_length
!= 0 or self
.max_length
!= sys
.maxint
or
256 self
.ref_table_name
is not None)
258 def without_constraints(self
):
259 return BaseType(self
.type)
262 def get_enum_type(atomic_type
):
263 """Returns the type of the 'enum' member for a BaseType whose
264 'type' is 'atomic_type'."""
265 return Type(BaseType(atomic_type
), None, 1, sys
.maxint
)
268 return self
.type == UuidType
and self
.ref_table_name
is not None
270 def is_strong_ref(self
):
271 return self
.is_ref() and self
.ref_type
== 'strong'
273 def is_weak_ref(self
):
274 return self
.is_ref() and self
.ref_type
== 'weak'
276 def toEnglish(self
, escapeLiteral
=returnUnchanged
):
277 if self
.type == UuidType
and self
.ref_table_name
:
278 s
= escapeLiteral(self
.ref_table_name
)
279 if self
.ref_type
== 'weak':
280 s
= "weak reference to " + s
283 return self
.type.to_string()
285 def constraintsToEnglish(self
, escapeLiteral
=returnUnchanged
):
287 literals
= [value
.toEnglish(escapeLiteral
)
288 for value
in self
.enum
.values
]
289 if len(literals
) == 2:
290 english
= 'either %s or %s' % (literals
[0], literals
[1])
292 english
= 'one of %s, %s, or %s' % (literals
[0],
293 ', '.join(literals
[1:-1]),
295 elif self
.min is not None and self
.max is not None:
296 if self
.type == IntegerType
:
297 english
= 'in range %s to %s' % (commafy(self
.min),
300 english
= 'in range %g to %g' % (self
.min, self
.max)
301 elif self
.min is not None:
302 if self
.type == IntegerType
:
303 english
= 'at least %s' % commafy(self
.min)
305 english
= 'at least %g' % self
.min
306 elif self
.max is not None:
307 if self
.type == IntegerType
:
308 english
= 'at most %s' % commafy(self
.max)
310 english
= 'at most %g' % self
.max
311 elif self
.min_length
!= 0 and self
.max_length
!= sys
.maxint
:
312 if self
.min_length
== self
.max_length
:
313 english
= 'exactly %d characters long' % (self
.min_length
)
315 english
= ('between %d and %d characters long'
316 % (self
.min_length
, self
.max_length
))
317 elif self
.min_length
!= 0:
318 return 'at least %d characters long' % self
.min_length
319 elif self
.max_length
!= sys
.maxint
:
320 english
= 'at most %d characters long' % self
.max_length
326 def toCType(self
, prefix
):
327 if self
.ref_table_name
:
328 return "struct %s%s *" % (prefix
, self
.ref_table_name
.lower())
330 return {IntegerType
: 'int64_t ',
332 UuidType
: 'struct uuid ',
333 BooleanType
: 'bool ',
334 StringType
: 'char *'}[self
.type]
336 def toAtomicType(self
):
337 return "OVSDB_TYPE_%s" % self
.type.to_string().upper()
339 def copyCValue(self
, dst
, src
):
340 args
= {'dst': dst
, 'src': src
}
341 if self
.ref_table_name
:
342 return ("%(dst)s = %(src)s->header_.uuid;") % args
343 elif self
.type == StringType
:
344 return "%(dst)s = xstrdup(%(src)s);" % args
346 return "%(dst)s = %(src)s;" % args
348 def initCDefault(self
, var
, is_optional
):
349 if self
.ref_table_name
:
350 return "%s = NULL;" % var
351 elif self
.type == StringType
and not is_optional
:
352 return '%s = "";' % var
354 pattern
= {IntegerType
: '%s = 0;',
355 RealType
: '%s = 0.0;',
356 UuidType
: 'uuid_zero(&%s);',
357 BooleanType
: '%s = false;',
358 StringType
: '%s = NULL;'}[self
.type]
361 def cInitBaseType(self
, indent
, var
):
363 stmts
.append('ovsdb_base_type_init(&%s, %s);' % (
364 var
, self
.toAtomicType()))
366 stmts
.append("%s.enum_ = xmalloc(sizeof *%s.enum_);"
368 stmts
+= self
.enum
.cInitDatum("%s.enum_" % var
)
369 if self
.type == IntegerType
:
370 if self
.min is not None:
371 stmts
.append('%s.u.integer.min = INT64_C(%d);'
373 if self
.max is not None:
374 stmts
.append('%s.u.integer.max = INT64_C(%d);'
376 elif self
.type == RealType
:
377 if self
.min is not None:
378 stmts
.append('%s.u.real.min = %d;' % (var
, self
.min))
379 if self
.max is not None:
380 stmts
.append('%s.u.real.max = %d;' % (var
, self
.max))
381 elif self
.type == StringType
:
382 if self
.min_length
is not None:
383 stmts
.append('%s.u.string.minLen = %d;'
384 % (var
, self
.min_length
))
385 if self
.max_length
!= sys
.maxint
:
386 stmts
.append('%s.u.string.maxLen = %d;'
387 % (var
, self
.max_length
))
388 elif self
.type == UuidType
:
389 if self
.ref_table_name
is not None:
390 stmts
.append('%s.u.uuid.refTableName = "%s";'
391 % (var
, escapeCString(self
.ref_table_name
)))
392 stmts
.append('%s.u.uuid.refType = OVSDB_REF_%s;'
393 % (var
, self
.ref_type
.upper()))
394 return '\n'.join([indent
+ stmt
for stmt
in stmts
])
401 def __init__(self
, key
, value
=None, n_min
=DEFAULT_MIN
, n_max
=DEFAULT_MAX
):
408 if self
.value
is None:
411 value
= self
.value
.copy()
412 return Type(self
.key
.copy(), value
, self
.n_min
, self
.n_max
)
414 def __eq__(self
, other
):
415 if not isinstance(other
, Type
):
416 return NotImplemented
417 return (self
.key
== other
.key
and self
.value
== other
.value
and
418 self
.n_min
== other
.n_min
and self
.n_max
== other
.n_max
)
420 def __ne__(self
, other
):
421 if not isinstance(other
, Type
):
422 return NotImplemented
424 return not (self
== other
)
427 return (self
.key
.type != VoidType
and self
.key
.is_valid() and
428 (self
.value
is None or
429 (self
.value
.type != VoidType
and self
.value
.is_valid())) and
430 self
.n_min
<= 1 <= self
.n_max
)
433 return self
.n_min
== 1 and self
.n_max
== 1 and not self
.value
435 def is_optional(self
):
436 return self
.n_min
== 0 and self
.n_max
== 1
438 def is_composite(self
):
439 return self
.n_max
> 1
442 return self
.value
is None and (self
.n_min
!= 1 or self
.n_max
!= 1)
445 return self
.value
is not None
447 def is_optional_pointer(self
):
448 return (self
.is_optional() and not self
.value
449 and (self
.key
.type == StringType
or self
.key
.ref_table_name
))
452 def __n_from_json(json
, default
):
455 elif type(json
) == int and 0 <= json
<= sys
.maxint
:
458 raise error
.Error("bad min or max value", json
)
462 if type(json
) in [str, unicode]:
463 return Type(BaseType
.from_json(json
))
465 parser
= ovs
.db
.parser
.Parser(json
, "ovsdb type")
466 key_json
= parser
.get("key", [dict, str, unicode])
467 value_json
= parser
.get_optional("value", [dict, str, unicode])
468 min_json
= parser
.get_optional("min", [int])
469 max_json
= parser
.get_optional("max", [int, str, unicode])
472 key
= BaseType
.from_json(key_json
)
474 value
= BaseType
.from_json(value_json
)
478 n_min
= Type
.__n
_from
_json
(min_json
, Type
.DEFAULT_MIN
)
480 if max_json
== 'unlimited':
483 n_max
= Type
.__n
_from
_json
(max_json
, Type
.DEFAULT_MAX
)
485 type_
= Type(key
, value
, n_min
, n_max
)
486 if not type_
.is_valid():
487 raise error
.Error("ovsdb type fails constraint checks", json
)
491 if self
.is_scalar() and not self
.key
.has_constraints():
492 return self
.key
.to_json()
494 json
= {"key": self
.key
.to_json()}
495 if self
.value
is not None:
496 json
["value"] = self
.value
.to_json()
497 if self
.n_min
!= Type
.DEFAULT_MIN
:
498 json
["min"] = self
.n_min
499 if self
.n_max
== sys
.maxint
:
500 json
["max"] = "unlimited"
501 elif self
.n_max
!= Type
.DEFAULT_MAX
:
502 json
["max"] = self
.n_max
505 def toEnglish(self
, escapeLiteral
=returnUnchanged
):
506 keyName
= self
.key
.toEnglish(escapeLiteral
)
508 valueName
= self
.value
.toEnglish(escapeLiteral
)
512 elif self
.is_optional():
514 return "optional %s-%s pair" % (keyName
, valueName
)
516 return "optional %s" % keyName
518 if self
.n_max
== sys
.maxint
:
520 quantity
= "%d or more " % self
.n_min
524 quantity
= "%d to %d " % (self
.n_min
, self
.n_max
)
526 quantity
= "up to %d " % self
.n_max
529 return "map of %s%s-%s pairs" % (quantity
, keyName
, valueName
)
531 if keyName
.endswith('s'):
532 plural
= keyName
+ "es"
534 plural
= keyName
+ "s"
535 return "set of %s%s" % (quantity
, plural
)
537 def constraintsToEnglish(self
, escapeLiteral
=returnUnchanged
):
539 keyConstraints
= self
.key
.constraintsToEnglish(escapeLiteral
)
542 constraints
.append('key %s' % keyConstraints
)
544 constraints
.append(keyConstraints
)
547 valueConstraints
= self
.value
.constraintsToEnglish(escapeLiteral
)
549 constraints
.append('value %s' % valueConstraints
)
551 return ', '.join(constraints
)
553 def cDeclComment(self
):
554 if self
.n_min
== 1 and self
.n_max
== 1 and self
.key
.type == StringType
:
555 return "\t/* Always nonnull. */"
559 def cInitType(self
, indent
, var
):
560 initKey
= self
.key
.cInitBaseType(indent
, "%s.key" % var
)
562 initValue
= self
.value
.cInitBaseType(indent
, "%s.value" % var
)
564 initValue
= ('%sovsdb_base_type_init(&%s.value, '
565 'OVSDB_TYPE_VOID);' % (indent
, var
))
566 initMin
= "%s%s.n_min = %s;" % (indent
, var
, self
.n_min
)
567 if self
.n_max
== sys
.maxint
:
571 initMax
= "%s%s.n_max = %s;" % (indent
, var
, n_max
)
572 return "\n".join((initKey
, initValue
, initMin
, initMax
))