]> git.proxmox.com Git - mirror_ovs.git/blob - tests/test-ovsdb.py
packets: Change IPv6 functions to more closely resemble IPv4 ones.
[mirror_ovs.git] / tests / test-ovsdb.py
1 # Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
2 #
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:
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 import getopt
16 import re
17 import os
18 import signal
19 import sys
20 import uuid
21
22 from ovs.db import error
23 import ovs.db.idl
24 import ovs.db.schema
25 from ovs.db import data
26 from ovs.db import types
27 import ovs.ovsuuid
28 import ovs.poller
29 import ovs.util
30
31
32 def unbox_json(json):
33 if type(json) == list and len(json) == 1:
34 return json[0]
35 else:
36 return json
37
38
39 def do_default_atoms():
40 for type_ in types.ATOMIC_TYPES:
41 if type_ == types.VoidType:
42 continue
43
44 sys.stdout.write("%s: " % type_.to_string())
45
46 atom = data.Atom.default(type_)
47 if atom != data.Atom.default(type_):
48 sys.stdout.write("wrong\n")
49 sys.exit(1)
50
51 sys.stdout.write("OK\n")
52
53
54 def do_default_data():
55 any_errors = False
56 for n_min in 0, 1:
57 for key in types.ATOMIC_TYPES:
58 if key == types.VoidType:
59 continue
60 for value in types.ATOMIC_TYPES:
61 if value == types.VoidType:
62 valueBase = None
63 else:
64 valueBase = types.BaseType(value)
65 type_ = types.Type(types.BaseType(key), valueBase, n_min, 1)
66 assert type_.is_valid()
67
68 sys.stdout.write("key %s, value %s, n_min %d: "
69 % (key.to_string(), value.to_string(), n_min))
70
71 datum = data.Datum.default(type_)
72 if datum != data.Datum.default(type_):
73 sys.stdout.write("wrong\n")
74 any_errors = True
75 else:
76 sys.stdout.write("OK\n")
77 if any_errors:
78 sys.exit(1)
79
80
81 def do_parse_atomic_type(type_string):
82 type_json = unbox_json(ovs.json.from_string(type_string))
83 atomic_type = types.AtomicType.from_json(type_json)
84 print ovs.json.to_string(atomic_type.to_json(), sort_keys=True)
85
86
87 def do_parse_base_type(type_string):
88 type_json = unbox_json(ovs.json.from_string(type_string))
89 base_type = types.BaseType.from_json(type_json)
90 print ovs.json.to_string(base_type.to_json(), sort_keys=True)
91
92
93 def do_parse_type(type_string):
94 type_json = unbox_json(ovs.json.from_string(type_string))
95 type_ = types.Type.from_json(type_json)
96 print ovs.json.to_string(type_.to_json(), sort_keys=True)
97
98
99 def do_parse_atoms(type_string, *atom_strings):
100 type_json = unbox_json(ovs.json.from_string(type_string))
101 base = types.BaseType.from_json(type_json)
102 for atom_string in atom_strings:
103 atom_json = unbox_json(ovs.json.from_string(atom_string))
104 try:
105 atom = data.Atom.from_json(base, atom_json)
106 print ovs.json.to_string(atom.to_json())
107 except error.Error, e:
108 print e.args[0].encode("utf8")
109
110
111 def do_parse_data(type_string, *data_strings):
112 type_json = unbox_json(ovs.json.from_string(type_string))
113 type_ = types.Type.from_json(type_json)
114 for datum_string in data_strings:
115 datum_json = unbox_json(ovs.json.from_string(datum_string))
116 datum = data.Datum.from_json(type_, datum_json)
117 print ovs.json.to_string(datum.to_json())
118
119
120 def do_sort_atoms(type_string, atom_strings):
121 type_json = unbox_json(ovs.json.from_string(type_string))
122 base = types.BaseType.from_json(type_json)
123 atoms = [data.Atom.from_json(base, atom_json)
124 for atom_json in unbox_json(ovs.json.from_string(atom_strings))]
125 print ovs.json.to_string([data.Atom.to_json(atom)
126 for atom in sorted(atoms)])
127
128
129 def do_parse_column(name, column_string):
130 column_json = unbox_json(ovs.json.from_string(column_string))
131 column = ovs.db.schema.ColumnSchema.from_json(column_json, name)
132 print ovs.json.to_string(column.to_json(), sort_keys=True)
133
134
135 def do_parse_table(name, table_string, default_is_root_string='false'):
136 default_is_root = default_is_root_string == 'true'
137 table_json = unbox_json(ovs.json.from_string(table_string))
138 table = ovs.db.schema.TableSchema.from_json(table_json, name)
139 print ovs.json.to_string(table.to_json(default_is_root), sort_keys=True)
140
141
142 def do_parse_schema(schema_string):
143 schema_json = unbox_json(ovs.json.from_string(schema_string))
144 schema = ovs.db.schema.DbSchema.from_json(schema_json)
145 print ovs.json.to_string(schema.to_json(), sort_keys=True)
146
147
148 def print_idl(idl, step):
149 n = 0
150 if "simple" in idl.tables:
151 simple_columns = ["i", "r", "b", "s", "u", "ia",
152 "ra", "ba", "sa", "ua", "uuid"]
153 simple = idl.tables["simple"].rows
154 for row in simple.itervalues():
155 s = "%03d:" % step
156 for column in simple_columns:
157 if hasattr(row, column) and not (type(getattr(row, column))
158 is ovs.db.data.Atom):
159 s += " %s=%s" % (column, getattr(row, column))
160 s = re.sub('""|,|u?\'', "", s)
161 s = re.sub('UUID\(([^)]+)\)', r'\1', s)
162 s = re.sub('False', 'false', s)
163 s = re.sub('True', 'true', s)
164 s = re.sub(r'(ba)=([^[][^ ]*) ', r'\1=[\2] ', s)
165 print(s)
166 n += 1
167
168 if "link1" in idl.tables:
169 l1 = idl.tables["link1"].rows
170 for row in l1.itervalues():
171 s = ["%03d: i=%s k=" % (step, row.i)]
172 if hasattr(row, "k") and row.k:
173 s.append(str(row.k.i))
174 if hasattr(row, "ka"):
175 s.append(" ka=[")
176 s.append(' '.join(sorted(str(ka.i) for ka in row.ka)))
177 s.append("] l2=")
178 if hasattr(row, "l2") and row.l2:
179 s.append(str(row.l2[0].i))
180 if hasattr(row, "uuid"):
181 s.append(" uuid=%s" % row.uuid)
182 print(''.join(s))
183 n += 1
184
185 if "link2" in idl.tables:
186 l2 = idl.tables["link2"].rows
187 for row in l2.itervalues():
188 s = ["%03d:" % step]
189 s.append(" i=%s l1=" % row.i)
190 if hasattr(row, "l1") and row.l1:
191 s.append(str(row.l1[0].i))
192 if hasattr(row, "uuid"):
193 s.append(" uuid=%s" % row.uuid)
194 print(''.join(s))
195 n += 1
196
197 if not n:
198 print("%03d: empty" % step)
199 sys.stdout.flush()
200
201
202 def substitute_uuids(json, symtab):
203 if type(json) in [str, unicode]:
204 symbol = symtab.get(json)
205 if symbol:
206 return str(symbol)
207 elif type(json) == list:
208 return [substitute_uuids(element, symtab) for element in json]
209 elif type(json) == dict:
210 d = {}
211 for key, value in json.iteritems():
212 d[key] = substitute_uuids(value, symtab)
213 return d
214 return json
215
216
217 def parse_uuids(json, symtab):
218 if type(json) in [str, unicode] and ovs.ovsuuid.is_valid_string(json):
219 name = "#%d#" % len(symtab)
220 sys.stderr.write("%s = %s\n" % (name, json))
221 symtab[name] = json
222 elif type(json) == list:
223 for element in json:
224 parse_uuids(element, symtab)
225 elif type(json) == dict:
226 for value in json.itervalues():
227 parse_uuids(value, symtab)
228
229
230 def idltest_find_simple(idl, i):
231 for row in idl.tables["simple"].rows.itervalues():
232 if row.i == i:
233 return row
234 return None
235
236
237 def idl_set(idl, commands, step):
238 txn = ovs.db.idl.Transaction(idl)
239 increment = False
240 fetch_cmds = []
241 events = []
242 for command in commands.split(','):
243 words = command.split()
244 name = words[0]
245 args = words[1:]
246
247 if name == "notifytest":
248 name = args[0]
249 args = args[1:]
250 old_notify = idl.notify
251
252 def notify(event, row, updates=None):
253 if updates:
254 upcol = updates._data.keys()[0]
255 else:
256 upcol = None
257 events.append("%s|%s|%s" % (event, row.i, upcol))
258 idl.notify = old_notify
259
260 idl.notify = notify
261
262 if name == "set":
263 if len(args) != 3:
264 sys.stderr.write('"set" command requires 3 arguments\n')
265 sys.exit(1)
266
267 s = idltest_find_simple(idl, int(args[0]))
268 if not s:
269 sys.stderr.write('"set" command asks for nonexistent i=%d\n'
270 % int(args[0]))
271 sys.exit(1)
272
273 if args[1] == "b":
274 s.b = args[2] == "1"
275 elif args[1] == "s":
276 s.s = args[2]
277 elif args[1] == "u":
278 s.u = uuid.UUID(args[2])
279 elif args[1] == "r":
280 s.r = float(args[2])
281 else:
282 sys.stderr.write('"set" comamnd asks for unknown column %s\n'
283 % args[2])
284 sys.stderr.exit(1)
285 elif name == "insert":
286 if len(args) != 1:
287 sys.stderr.write('"set" command requires 1 argument\n')
288 sys.exit(1)
289
290 s = txn.insert(idl.tables["simple"])
291 s.i = int(args[0])
292 elif name == "delete":
293 if len(args) != 1:
294 sys.stderr.write('"delete" command requires 1 argument\n')
295 sys.exit(1)
296
297 s = idltest_find_simple(idl, int(args[0]))
298 if not s:
299 sys.stderr.write('"delete" command asks for nonexistent i=%d\n'
300 % int(args[0]))
301 sys.exit(1)
302 s.delete()
303 elif name == "verify":
304 if len(args) != 2:
305 sys.stderr.write('"verify" command requires 2 arguments\n')
306 sys.exit(1)
307
308 s = idltest_find_simple(idl, int(args[0]))
309 if not s:
310 sys.stderr.write('"verify" command asks for nonexistent i=%d\n'
311 % int(args[0]))
312 sys.exit(1)
313
314 if args[1] in ("i", "b", "s", "u", "r"):
315 s.verify(args[1])
316 else:
317 sys.stderr.write('"verify" command asks for unknown column '
318 '"%s"\n' % args[1])
319 sys.exit(1)
320 elif name == "fetch":
321 if len(args) != 2:
322 sys.stderr.write('"fetch" command requires 2 argument\n')
323 sys.exit(1)
324
325 row = idltest_find_simple(idl, int(args[0]))
326 if not row:
327 sys.stderr.write('"fetch" command asks for nonexistent i=%d\n'
328 % int(args[0]))
329 sys.exit(1)
330
331 column = args[1]
332 row.fetch(column)
333 fetch_cmds.append([row, column])
334 elif name == "increment":
335 if len(args) != 1:
336 sys.stderr.write('"increment" command requires 1 argument\n')
337 sys.exit(1)
338
339 s = idltest_find_simple(idl, int(args[0]))
340 if not s:
341 sys.stderr.write('"set" command asks for nonexistent i=%d\n'
342 % int(args[0]))
343 sys.exit(1)
344
345 s.increment("i")
346 increment = True
347 elif name == "abort":
348 txn.abort()
349 break
350 elif name == "destroy":
351 print "%03d: destroy" % step
352 sys.stdout.flush()
353 txn.abort()
354 return
355 elif name == "linktest":
356 l1_0 = txn.insert(idl.tables["link1"])
357 l1_0.i = 1
358 l1_0.k = [l1_0]
359 l1_0.ka = [l1_0]
360 l1_1 = txn.insert(idl.tables["link1"])
361 l1_1.i = 2
362 l1_1.k = [l1_0]
363 l1_1.ka = [l1_0, l1_1]
364 elif name == 'getattrtest':
365 l1 = txn.insert(idl.tables["link1"])
366 i = getattr(l1, 'i', 1)
367 assert i == 1
368 l1.i = 2
369 i = getattr(l1, 'i', 1)
370 assert i == 2
371 l1.k = [l1]
372 else:
373 sys.stderr.write("unknown command %s\n" % name)
374 sys.exit(1)
375
376 status = txn.commit_block()
377 sys.stdout.write("%03d: commit, status=%s"
378 % (step, ovs.db.idl.Transaction.status_to_string(status)))
379 if increment and status == ovs.db.idl.Transaction.SUCCESS:
380 sys.stdout.write(", increment=%d" % txn.get_increment_new_value())
381 if events:
382 # Event notifications from operations in a single transaction are
383 # not in a gauranteed order due to update messages being dicts
384 sys.stdout.write(", events=" + ", ".join(sorted(events)))
385 sys.stdout.write("\n")
386 sys.stdout.flush()
387
388
389 def do_idl(schema_file, remote, *commands):
390 schema_helper = ovs.db.idl.SchemaHelper(schema_file)
391 if commands and commands[0].startswith("?"):
392 monitor = {}
393 readonly = {}
394 for x in commands[0][1:].split("?"):
395 readonly = []
396 table, columns = x.split(":")
397 columns = columns.split(",")
398 for index, column in enumerate(columns):
399 if column[-1] == '!':
400 columns[index] = columns[index][:-1]
401 readonly.append(columns[index])
402 schema_helper.register_columns(table, columns, readonly)
403 commands = commands[1:]
404 else:
405 schema_helper.register_all()
406 idl = ovs.db.idl.Idl(remote, schema_helper)
407
408 if commands:
409 error, stream = ovs.stream.Stream.open_block(
410 ovs.stream.Stream.open(remote))
411 if error:
412 sys.stderr.write("failed to connect to \"%s\"" % remote)
413 sys.exit(1)
414 rpc = ovs.jsonrpc.Connection(stream)
415 else:
416 rpc = None
417
418 symtab = {}
419 seqno = 0
420 step = 0
421 for command in commands:
422 if command.startswith("+"):
423 # The previous transaction didn't change anything.
424 command = command[1:]
425 else:
426 # Wait for update.
427 while idl.change_seqno == seqno and not idl.run():
428 rpc.run()
429
430 poller = ovs.poller.Poller()
431 idl.wait(poller)
432 rpc.wait(poller)
433 poller.block()
434
435 print_idl(idl, step)
436 step += 1
437
438 seqno = idl.change_seqno
439
440 if command == "reconnect":
441 print("%03d: reconnect" % step)
442 sys.stdout.flush()
443 step += 1
444 idl.force_reconnect()
445 elif not command.startswith("["):
446 idl_set(idl, command, step)
447 step += 1
448 else:
449 json = ovs.json.from_string(command)
450 if type(json) in [str, unicode]:
451 sys.stderr.write("\"%s\": %s\n" % (command, json))
452 sys.exit(1)
453 json = substitute_uuids(json, symtab)
454 request = ovs.jsonrpc.Message.create_request("transact", json)
455 error, reply = rpc.transact_block(request)
456 if error:
457 sys.stderr.write("jsonrpc transaction failed: %s"
458 % os.strerror(error))
459 sys.exit(1)
460 elif reply.error is not None:
461 sys.stderr.write("jsonrpc transaction failed: %s"
462 % reply.error)
463 sys.exit(1)
464
465 sys.stdout.write("%03d: " % step)
466 sys.stdout.flush()
467 step += 1
468 if reply.result is not None:
469 parse_uuids(reply.result, symtab)
470 reply.id = None
471 sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json()))
472 sys.stdout.flush()
473
474 if rpc:
475 rpc.close()
476 while idl.change_seqno == seqno and not idl.run():
477 poller = ovs.poller.Poller()
478 idl.wait(poller)
479 poller.block()
480 print_idl(idl, step)
481 step += 1
482 idl.close()
483 print("%03d: done" % step)
484
485
486 def usage():
487 print """\
488 %(program_name)s: test utility for Open vSwitch database Python bindings
489 usage: %(program_name)s [OPTIONS] COMMAND ARG...
490
491 The following commands are supported:
492 default-atoms
493 test ovsdb_atom_default()
494 default-data
495 test ovsdb_datum_default()
496 parse-atomic-type TYPE
497 parse TYPE as OVSDB atomic type, and re-serialize
498 parse-base-type TYPE
499 parse TYPE as OVSDB base type, and re-serialize
500 parse-type JSON
501 parse JSON as OVSDB type, and re-serialize
502 parse-atoms TYPE ATOM...
503 parse JSON ATOMs as atoms of TYPE, and re-serialize
504 parse-atom-strings TYPE ATOM...
505 parse string ATOMs as atoms of given TYPE, and re-serialize
506 sort-atoms TYPE ATOM...
507 print JSON ATOMs in sorted order
508 parse-data TYPE DATUM...
509 parse JSON DATUMs as data of given TYPE, and re-serialize
510 parse-column NAME OBJECT
511 parse column NAME with info OBJECT, and re-serialize
512 parse-table NAME OBJECT [DEFAULT-IS-ROOT]
513 parse table NAME with info OBJECT
514 parse-schema JSON
515 parse JSON as an OVSDB schema, and re-serialize
516 idl SCHEMA SERVER [?T1:C1,C2...[?T2:C1,C2,...]...] [TRANSACTION...]
517 connect to SERVER (which has the specified SCHEMA) and dump the
518 contents of the database as seen initially by the IDL implementation
519 and after executing each TRANSACTION. (Each TRANSACTION must modify
520 the database or this command will hang.)
521 By default, all columns of all tables are monitored. The "?" option
522 can be used to monitor specific Table:Column(s). The table and their
523 columns are listed as a string of the form starting with "?":
524 ?<table-name>:<column-name>,<column-name>,...
525 e.g.:
526 ?simple:b - Monitor column "b" in table "simple"
527 Entries for multiple tables are seperated by "?":
528 ?<table-name>:<column-name>,...?<table-name>:<column-name>,...
529 e.g.:
530 ?simple:b?link1:i,k - Monitor column "b" in table "simple",
531 and column "i", "k" in table "link1"
532 Readonly columns: Suffixing a "!" after a column indicates that the
533 column is to be registered "readonly".
534 e.g.:
535 ?simple:i,b! - Register interest in column "i" (monitoring) and
536 column "b" (readonly).
537
538
539 The following options are also available:
540 -t, --timeout=SECS give up after SECS seconds
541 -h, --help display this help message\
542 """ % {'program_name': ovs.util.PROGRAM_NAME}
543 sys.exit(0)
544
545
546 def main(argv):
547 try:
548 options, args = getopt.gnu_getopt(argv[1:], 't:h',
549 ['timeout',
550 'help'])
551 except getopt.GetoptError, geo:
552 sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
553 sys.exit(1)
554
555 for key, value in options:
556 if key in ['-h', '--help']:
557 usage()
558 elif key in ['-t', '--timeout']:
559 try:
560 timeout = int(value)
561 if timeout < 1:
562 raise TypeError
563 except TypeError:
564 raise error.Error("value %s on -t or --timeout is not at "
565 "least 1" % value)
566 signal.alarm(timeout)
567 else:
568 sys.exit(0)
569
570 if not args:
571 sys.stderr.write("%s: missing command argument "
572 "(use --help for help)\n" % ovs.util.PROGRAM_NAME)
573 sys.exit(1)
574
575 commands = {"default-atoms": (do_default_atoms, 0),
576 "default-data": (do_default_data, 0),
577 "parse-atomic-type": (do_parse_atomic_type, 1),
578 "parse-base-type": (do_parse_base_type, 1),
579 "parse-type": (do_parse_type, 1),
580 "parse-atoms": (do_parse_atoms, (2,)),
581 "parse-data": (do_parse_data, (2,)),
582 "sort-atoms": (do_sort_atoms, 2),
583 "parse-column": (do_parse_column, 2),
584 "parse-table": (do_parse_table, (2, 3)),
585 "parse-schema": (do_parse_schema, 1),
586 "idl": (do_idl, (2,))}
587
588 command_name = args[0]
589 args = args[1:]
590 if not command_name in commands:
591 sys.stderr.write("%s: unknown command \"%s\" "
592 "(use --help for help)\n" % (ovs.util.PROGRAM_NAME,
593 command_name))
594 sys.exit(1)
595
596 func, n_args = commands[command_name]
597 if type(n_args) == tuple:
598 if len(args) < n_args[0]:
599 sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
600 "only %d provided\n"
601 % (ovs.util.PROGRAM_NAME, command_name,
602 n_args, len(args)))
603 sys.exit(1)
604 elif type(n_args) == int:
605 if len(args) != n_args:
606 sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
607 "provided\n"
608 % (ovs.util.PROGRAM_NAME, command_name,
609 n_args, len(args)))
610 sys.exit(1)
611 else:
612 assert False
613
614 func(*args)
615
616
617 if __name__ == '__main__':
618 try:
619 main(sys.argv)
620 except error.Error, e:
621 sys.stderr.write("%s\n" % e)
622 sys.exit(1)