vswitch.xml: Update docs for max-idle.
[cascardo/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     simple = idl.tables["simple"].rows
150     l1 = idl.tables["link1"].rows
151     l2 = idl.tables["link2"].rows
152
153     n = 0
154     for row in simple.itervalues():
155         s = ("%03d: i=%s r=%s b=%s s=%s u=%s "
156              "ia=%s ra=%s ba=%s sa=%s ua=%s uuid=%s"
157              % (step, row.i, row.r, row.b, row.s, row.u,
158                 row.ia, row.ra, row.ba, row.sa, row.ua, row.uuid))
159         s = re.sub('""|,|u?\'', "", s)
160         s = re.sub('UUID\(([^)]+)\)', r'\1', s)
161         s = re.sub('False', 'false', s)
162         s = re.sub('True', 'true', s)
163         s = re.sub(r'(ba)=([^[][^ ]*) ', r'\1=[\2] ', s)
164         print(s)
165         n += 1
166
167     for row in l1.itervalues():
168         s = ["%03d: i=%s k=" % (step, row.i)]
169         if row.k:
170             s.append(str(row.k.i))
171         s.append(" ka=[")
172         s.append(' '.join(sorted(str(ka.i) for ka in row.ka)))
173         s.append("] l2=")
174         if row.l2:
175             s.append(str(row.l2[0].i))
176         s.append(" uuid=%s" % row.uuid)
177         print(''.join(s))
178         n += 1
179
180     for row in l2.itervalues():
181         s = ["%03d: i=%s l1=" % (step, row.i)]
182         if row.l1:
183             s.append(str(row.l1[0].i))
184         s.append(" uuid=%s" % row.uuid)
185         print(''.join(s))
186         n += 1
187
188     if not n:
189         print("%03d: empty" % step)
190     sys.stdout.flush()
191
192
193 def substitute_uuids(json, symtab):
194     if type(json) in [str, unicode]:
195         symbol = symtab.get(json)
196         if symbol:
197             return str(symbol)
198     elif type(json) == list:
199         return [substitute_uuids(element, symtab) for element in json]
200     elif type(json) == dict:
201         d = {}
202         for key, value in json.iteritems():
203             d[key] = substitute_uuids(value, symtab)
204         return d
205     return json
206
207
208 def parse_uuids(json, symtab):
209     if type(json) in [str, unicode] and ovs.ovsuuid.is_valid_string(json):
210         name = "#%d#" % len(symtab)
211         sys.stderr.write("%s = %s\n" % (name, json))
212         symtab[name] = json
213     elif type(json) == list:
214         for element in json:
215             parse_uuids(element, symtab)
216     elif type(json) == dict:
217         for value in json.itervalues():
218             parse_uuids(value, symtab)
219
220
221 def idltest_find_simple(idl, i):
222     for row in idl.tables["simple"].rows.itervalues():
223         if row.i == i:
224             return row
225     return None
226
227
228 def idl_set(idl, commands, step):
229     txn = ovs.db.idl.Transaction(idl)
230     increment = False
231     events = []
232     for command in commands.split(','):
233         words = command.split()
234         name = words[0]
235         args = words[1:]
236
237         if name == "notifytest":
238             name = args[0]
239             args = args[1:]
240             old_notify = idl.notify
241
242             def notify(event, row, updates=None):
243                 if updates:
244                     upcol = updates._data.keys()[0]
245                 else:
246                     upcol = None
247                 events.append("%s|%s|%s" % (event, row.i, upcol))
248                 idl.notify = old_notify
249
250             idl.notify = notify
251
252         if name == "set":
253             if len(args) != 3:
254                 sys.stderr.write('"set" command requires 3 arguments\n')
255                 sys.exit(1)
256
257             s = idltest_find_simple(idl, int(args[0]))
258             if not s:
259                 sys.stderr.write('"set" command asks for nonexistent i=%d\n'
260                                  % int(args[0]))
261                 sys.exit(1)
262
263             if args[1] == "b":
264                 s.b = args[2] == "1"
265             elif args[1] == "s":
266                 s.s = args[2]
267             elif args[1] == "u":
268                 s.u = uuid.UUID(args[2])
269             elif args[1] == "r":
270                 s.r = float(args[2])
271             else:
272                 sys.stderr.write('"set" comamnd asks for unknown column %s\n'
273                                  % args[2])
274                 sys.stderr.exit(1)
275         elif name == "insert":
276             if len(args) != 1:
277                 sys.stderr.write('"set" command requires 1 argument\n')
278                 sys.exit(1)
279
280             s = txn.insert(idl.tables["simple"])
281             s.i = int(args[0])
282         elif name == "delete":
283             if len(args) != 1:
284                 sys.stderr.write('"delete" command requires 1 argument\n')
285                 sys.exit(1)
286
287             s = idltest_find_simple(idl, int(args[0]))
288             if not s:
289                 sys.stderr.write('"delete" command asks for nonexistent i=%d\n'
290                                  % int(args[0]))
291                 sys.exit(1)
292             s.delete()
293         elif name == "verify":
294             if len(args) != 2:
295                 sys.stderr.write('"verify" command requires 2 arguments\n')
296                 sys.exit(1)
297
298             s = idltest_find_simple(idl, int(args[0]))
299             if not s:
300                 sys.stderr.write('"verify" command asks for nonexistent i=%d\n'
301                                  % int(args[0]))
302                 sys.exit(1)
303
304             if args[1] in ("i", "b", "s", "u", "r"):
305                 s.verify(args[1])
306             else:
307                 sys.stderr.write('"verify" command asks for unknown column '
308                                  '"%s"\n' % args[1])
309                 sys.exit(1)
310         elif name == "increment":
311             if len(args) != 1:
312                 sys.stderr.write('"increment" command requires 1 argument\n')
313                 sys.exit(1)
314
315             s = idltest_find_simple(idl, int(args[0]))
316             if not s:
317                 sys.stderr.write('"set" command asks for nonexistent i=%d\n'
318                                  % int(args[0]))
319                 sys.exit(1)
320
321             s.increment("i")
322             increment = True
323         elif name == "abort":
324             txn.abort()
325             break
326         elif name == "destroy":
327             print "%03d: destroy" % step
328             sys.stdout.flush()
329             txn.abort()
330             return
331         elif name == "linktest":
332             l1_0 = txn.insert(idl.tables["link1"])
333             l1_0.i = 1
334             l1_0.k = [l1_0]
335             l1_0.ka = [l1_0]
336             l1_1 = txn.insert(idl.tables["link1"])
337             l1_1.i = 2
338             l1_1.k = [l1_0]
339             l1_1.ka = [l1_0, l1_1]
340         elif name == 'getattrtest':
341             l1 = txn.insert(idl.tables["link1"])
342             i = getattr(l1, 'i', 1)
343             assert i == 1
344             l1.i = 2
345             i = getattr(l1, 'i', 1)
346             assert i == 2
347             l1.k = [l1]
348         else:
349             sys.stderr.write("unknown command %s\n" % name)
350             sys.exit(1)
351
352     status = txn.commit_block()
353     sys.stdout.write("%03d: commit, status=%s"
354                      % (step, ovs.db.idl.Transaction.status_to_string(status)))
355     if increment and status == ovs.db.idl.Transaction.SUCCESS:
356         sys.stdout.write(", increment=%d" % txn.get_increment_new_value())
357     if events:
358         # Event notifications from operations in a single transaction are
359         # not in a gauranteed order due to update messages being dicts
360         sys.stdout.write(", events=" + ", ".join(sorted(events)))
361     sys.stdout.write("\n")
362     sys.stdout.flush()
363
364
365 def do_idl(schema_file, remote, *commands):
366     schema_helper = ovs.db.idl.SchemaHelper(schema_file)
367     if commands and commands[0].startswith("?"):
368         monitor = {}
369         for x in commands[0][1:].split("?"):
370             table, columns = x.split(":")
371             monitor[table] = columns.split(",")
372             schema_helper.register_columns(table, monitor[table])
373         commands = commands[1:]
374     else:
375         schema_helper.register_all()
376     idl = ovs.db.idl.Idl(remote, schema_helper)
377
378     if commands:
379         error, stream = ovs.stream.Stream.open_block(
380             ovs.stream.Stream.open(remote))
381         if error:
382             sys.stderr.write("failed to connect to \"%s\"" % remote)
383             sys.exit(1)
384         rpc = ovs.jsonrpc.Connection(stream)
385     else:
386         rpc = None
387
388     symtab = {}
389     seqno = 0
390     step = 0
391     for command in commands:
392         if command.startswith("+"):
393             # The previous transaction didn't change anything.
394             command = command[1:]
395         else:
396             # Wait for update.
397             while idl.change_seqno == seqno and not idl.run():
398                 rpc.run()
399
400                 poller = ovs.poller.Poller()
401                 idl.wait(poller)
402                 rpc.wait(poller)
403                 poller.block()
404
405             print_idl(idl, step)
406             step += 1
407
408         seqno = idl.change_seqno
409
410         if command == "reconnect":
411             print("%03d: reconnect" % step)
412             sys.stdout.flush()
413             step += 1
414             idl.force_reconnect()
415         elif not command.startswith("["):
416             idl_set(idl, command, step)
417             step += 1
418         else:
419             json = ovs.json.from_string(command)
420             if type(json) in [str, unicode]:
421                 sys.stderr.write("\"%s\": %s\n" % (command, json))
422                 sys.exit(1)
423             json = substitute_uuids(json, symtab)
424             request = ovs.jsonrpc.Message.create_request("transact", json)
425             error, reply = rpc.transact_block(request)
426             if error:
427                 sys.stderr.write("jsonrpc transaction failed: %s"
428                                  % os.strerror(error))
429                 sys.exit(1)
430             elif reply.error is not None:
431                 sys.stderr.write("jsonrpc transaction failed: %s"
432                                  % reply.error)
433                 sys.exit(1)
434
435             sys.stdout.write("%03d: " % step)
436             sys.stdout.flush()
437             step += 1
438             if reply.result is not None:
439                 parse_uuids(reply.result, symtab)
440             reply.id = None
441             sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json()))
442             sys.stdout.flush()
443
444     if rpc:
445         rpc.close()
446     while idl.change_seqno == seqno and not idl.run():
447         poller = ovs.poller.Poller()
448         idl.wait(poller)
449         poller.block()
450     print_idl(idl, step)
451     step += 1
452     idl.close()
453     print("%03d: done" % step)
454
455
456 def usage():
457     print """\
458 %(program_name)s: test utility for Open vSwitch database Python bindings
459 usage: %(program_name)s [OPTIONS] COMMAND ARG...
460
461 The following commands are supported:
462 default-atoms
463   test ovsdb_atom_default()
464 default-data
465   test ovsdb_datum_default()
466 parse-atomic-type TYPE
467   parse TYPE as OVSDB atomic type, and re-serialize
468 parse-base-type TYPE
469   parse TYPE as OVSDB base type, and re-serialize
470 parse-type JSON
471   parse JSON as OVSDB type, and re-serialize
472 parse-atoms TYPE ATOM...
473   parse JSON ATOMs as atoms of TYPE, and re-serialize
474 parse-atom-strings TYPE ATOM...
475   parse string ATOMs as atoms of given TYPE, and re-serialize
476 sort-atoms TYPE ATOM...
477   print JSON ATOMs in sorted order
478 parse-data TYPE DATUM...
479   parse JSON DATUMs as data of given TYPE, and re-serialize
480 parse-column NAME OBJECT
481   parse column NAME with info OBJECT, and re-serialize
482 parse-table NAME OBJECT [DEFAULT-IS-ROOT]
483   parse table NAME with info OBJECT
484 parse-schema JSON
485   parse JSON as an OVSDB schema, and re-serialize
486 idl SCHEMA SERVER [?T1:C1,C2...[?T2:C1,C2,...]...] [TRANSACTION...]
487   connect to SERVER (which has the specified SCHEMA) and dump the
488   contents of the database as seen initially by the IDL implementation
489   and after executing each TRANSACTION.  (Each TRANSACTION must modify
490   the database or this command will hang.)
491   By default, all columns of all tables are monitored. The "?" option
492   can be used to monitor specific Table:Column(s). The table and their
493   columns are listed as a string of the form starting with "?":
494       ?<table-name>:<column-name>,<column-name>,...
495   e.g.:
496       ?simple:b - Monitor column "b" in table "simple"
497   Entries for multiple tables are seperated by "?":
498       ?<table-name>:<column-name>,...?<table-name>:<column-name>,...
499   e.g.:
500       ?simple:b?link1:i,k - Monitor column "b" in table "simple",
501                             and column "i", "k" in table "link1"
502
503 The following options are also available:
504   -t, --timeout=SECS          give up after SECS seconds
505   -h, --help                  display this help message\
506 """ % {'program_name': ovs.util.PROGRAM_NAME}
507     sys.exit(0)
508
509
510 def main(argv):
511     try:
512         options, args = getopt.gnu_getopt(argv[1:], 't:h',
513                                           ['timeout',
514                                            'help'])
515     except getopt.GetoptError, geo:
516         sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
517         sys.exit(1)
518
519     for key, value in options:
520         if key in ['-h', '--help']:
521             usage()
522         elif key in ['-t', '--timeout']:
523             try:
524                 timeout = int(value)
525                 if timeout < 1:
526                     raise TypeError
527             except TypeError:
528                 raise error.Error("value %s on -t or --timeout is not at "
529                                   "least 1" % value)
530             signal.alarm(timeout)
531         else:
532             sys.exit(0)
533
534     if not args:
535         sys.stderr.write("%s: missing command argument "
536                          "(use --help for help)\n" % ovs.util.PROGRAM_NAME)
537         sys.exit(1)
538
539     commands = {"default-atoms": (do_default_atoms, 0),
540                 "default-data": (do_default_data, 0),
541                 "parse-atomic-type": (do_parse_atomic_type, 1),
542                 "parse-base-type": (do_parse_base_type, 1),
543                 "parse-type": (do_parse_type, 1),
544                 "parse-atoms": (do_parse_atoms, (2,)),
545                 "parse-data": (do_parse_data, (2,)),
546                 "sort-atoms": (do_sort_atoms, 2),
547                 "parse-column": (do_parse_column, 2),
548                 "parse-table": (do_parse_table, (2, 3)),
549                 "parse-schema": (do_parse_schema, 1),
550                 "idl": (do_idl, (2,))}
551
552     command_name = args[0]
553     args = args[1:]
554     if not command_name in commands:
555         sys.stderr.write("%s: unknown command \"%s\" "
556                          "(use --help for help)\n" % (ovs.util.PROGRAM_NAME,
557                                                       command_name))
558         sys.exit(1)
559
560     func, n_args = commands[command_name]
561     if type(n_args) == tuple:
562         if len(args) < n_args[0]:
563             sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
564                              "only %d provided\n"
565                              % (ovs.util.PROGRAM_NAME, command_name,
566                                 n_args, len(args)))
567             sys.exit(1)
568     elif type(n_args) == int:
569         if len(args) != n_args:
570             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
571                              "provided\n"
572                              % (ovs.util.PROGRAM_NAME, command_name,
573                                 n_args, len(args)))
574             sys.exit(1)
575     else:
576         assert False
577
578     func(*args)
579
580
581 if __name__ == '__main__':
582     try:
583         main(sys.argv)
584     except error.Error, e:
585         sys.stderr.write("%s\n" % e)
586         sys.exit(1)