python: Resolve a deprecation warning.
[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 import ovs.db.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 ovs.db.types.ATOMIC_TYPES:
41         if type_ == ovs.db.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 ovs.db.types.ATOMIC_TYPES:
58             if key == ovs.db.types.VoidType:
59                 continue
60             for value in ovs.db.types.ATOMIC_TYPES:
61                 if value == ovs.db.types.VoidType:
62                     valueBase = None
63                 else:
64                     valueBase = ovs.db.types.BaseType(value)
65                 type_ = ovs.db.types.Type(ovs.db.types.BaseType(key),
66                                           valueBase, n_min, 1)
67                 assert type_.is_valid()
68
69                 sys.stdout.write("key %s, value %s, n_min %d: "
70                                  % (key.to_string(), value.to_string(), n_min))
71
72                 datum = data.Datum.default(type_)
73                 if datum != data.Datum.default(type_):
74                     sys.stdout.write("wrong\n")
75                     any_errors = True
76                 else:
77                     sys.stdout.write("OK\n")
78     if any_errors:
79         sys.exit(1)
80
81
82 def do_parse_atomic_type(type_string):
83     type_json = unbox_json(ovs.json.from_string(type_string))
84     atomic_type = ovs.db.types.AtomicType.from_json(type_json)
85     print ovs.json.to_string(atomic_type.to_json(), sort_keys=True)
86
87
88 def do_parse_base_type(type_string):
89     type_json = unbox_json(ovs.json.from_string(type_string))
90     base_type = ovs.db.types.BaseType.from_json(type_json)
91     print ovs.json.to_string(base_type.to_json(), sort_keys=True)
92
93
94 def do_parse_type(type_string):
95     type_json = unbox_json(ovs.json.from_string(type_string))
96     type_ = ovs.db.types.Type.from_json(type_json)
97     print ovs.json.to_string(type_.to_json(), sort_keys=True)
98
99
100 def do_parse_atoms(type_string, *atom_strings):
101     type_json = unbox_json(ovs.json.from_string(type_string))
102     base = ovs.db.types.BaseType.from_json(type_json)
103     for atom_string in atom_strings:
104         atom_json = unbox_json(ovs.json.from_string(atom_string))
105         try:
106             atom = data.Atom.from_json(base, atom_json)
107             print ovs.json.to_string(atom.to_json())
108         except error.Error, e:
109             print e.args[0].encode("utf8")
110
111
112 def do_parse_data(type_string, *data_strings):
113     type_json = unbox_json(ovs.json.from_string(type_string))
114     type_ = ovs.db.types.Type.from_json(type_json)
115     for datum_string in data_strings:
116         datum_json = unbox_json(ovs.json.from_string(datum_string))
117         datum = data.Datum.from_json(type_, datum_json)
118         print ovs.json.to_string(datum.to_json())
119
120
121 def do_sort_atoms(type_string, atom_strings):
122     type_json = unbox_json(ovs.json.from_string(type_string))
123     base = ovs.db.types.BaseType.from_json(type_json)
124     atoms = [data.Atom.from_json(base, atom_json)
125              for atom_json in unbox_json(ovs.json.from_string(atom_strings))]
126     print ovs.json.to_string([data.Atom.to_json(atom)
127                               for atom in sorted(atoms)])
128
129
130 def do_parse_column(name, column_string):
131     column_json = unbox_json(ovs.json.from_string(column_string))
132     column = ovs.db.schema.ColumnSchema.from_json(column_json, name)
133     print ovs.json.to_string(column.to_json(), sort_keys=True)
134
135
136 def do_parse_table(name, table_string, default_is_root_string='false'):
137     default_is_root = default_is_root_string == 'true'
138     table_json = unbox_json(ovs.json.from_string(table_string))
139     table = ovs.db.schema.TableSchema.from_json(table_json, name)
140     print ovs.json.to_string(table.to_json(default_is_root), sort_keys=True)
141
142
143 def do_parse_schema(schema_string):
144     schema_json = unbox_json(ovs.json.from_string(schema_string))
145     schema = ovs.db.schema.DbSchema.from_json(schema_json)
146     print ovs.json.to_string(schema.to_json(), sort_keys=True)
147
148
149 def print_idl(idl, step):
150     n = 0
151     if "simple" in idl.tables:
152         simple_columns = ["i", "r", "b", "s", "u", "ia",
153                           "ra", "ba", "sa", "ua", "uuid"]
154         simple = idl.tables["simple"].rows
155         for row in simple.itervalues():
156             s = "%03d:" % step
157             for column in simple_columns:
158                 if hasattr(row, column) and not (type(getattr(row, column))
159                                                  is ovs.db.data.Atom):
160                     s += " %s=%s" % (column, getattr(row, column))
161             s = re.sub('""|,|u?\'', "", s)
162             s = re.sub('UUID\(([^)]+)\)', r'\1', s)
163             s = re.sub('False', 'false', s)
164             s = re.sub('True', 'true', s)
165             s = re.sub(r'(ba)=([^[][^ ]*) ', r'\1=[\2] ', s)
166             print(s)
167             n += 1
168
169     if "link1" in idl.tables:
170         l1 = idl.tables["link1"].rows
171         for row in l1.itervalues():
172             s = ["%03d: i=%s k=" % (step, row.i)]
173             if hasattr(row, "k") and row.k:
174                 s.append(str(row.k.i))
175             if hasattr(row, "ka"):
176                 s.append(" ka=[")
177                 s.append(' '.join(sorted(str(ka.i) for ka in row.ka)))
178                 s.append("] l2=")
179             if hasattr(row, "l2") and row.l2:
180                 s.append(str(row.l2[0].i))
181             if hasattr(row, "uuid"):
182                 s.append(" uuid=%s" % row.uuid)
183             print(''.join(s))
184             n += 1
185
186     if "link2" in idl.tables:
187         l2 = idl.tables["link2"].rows
188         for row in l2.itervalues():
189             s = ["%03d:" % step]
190             s.append(" i=%s l1=" % row.i)
191             if hasattr(row, "l1") and row.l1:
192                 s.append(str(row.l1[0].i))
193             if hasattr(row, "uuid"):
194                 s.append(" uuid=%s" % row.uuid)
195             print(''.join(s))
196             n += 1
197
198     if not n:
199         print("%03d: empty" % step)
200     sys.stdout.flush()
201
202
203 def substitute_uuids(json, symtab):
204     if type(json) in [str, unicode]:
205         symbol = symtab.get(json)
206         if symbol:
207             return str(symbol)
208     elif type(json) == list:
209         return [substitute_uuids(element, symtab) for element in json]
210     elif type(json) == dict:
211         d = {}
212         for key, value in json.iteritems():
213             d[key] = substitute_uuids(value, symtab)
214         return d
215     return json
216
217
218 def parse_uuids(json, symtab):
219     if type(json) in [str, unicode] and ovs.ovsuuid.is_valid_string(json):
220         name = "#%d#" % len(symtab)
221         sys.stderr.write("%s = %s\n" % (name, json))
222         symtab[name] = json
223     elif type(json) == list:
224         for element in json:
225             parse_uuids(element, symtab)
226     elif type(json) == dict:
227         for value in json.itervalues():
228             parse_uuids(value, symtab)
229
230
231 def idltest_find_simple(idl, i):
232     for row in idl.tables["simple"].rows.itervalues():
233         if row.i == i:
234             return row
235     return None
236
237
238 def idl_set(idl, commands, step):
239     txn = ovs.db.idl.Transaction(idl)
240     increment = False
241     fetch_cmds = []
242     events = []
243     for command in commands.split(','):
244         words = command.split()
245         name = words[0]
246         args = words[1:]
247
248         if name == "notifytest":
249             name = args[0]
250             args = args[1:]
251             old_notify = idl.notify
252
253             def notify(event, row, updates=None):
254                 if updates:
255                     upcol = updates._data.keys()[0]
256                 else:
257                     upcol = None
258                 events.append("%s|%s|%s" % (event, row.i, upcol))
259                 idl.notify = old_notify
260
261             idl.notify = notify
262
263         if name == "set":
264             if len(args) != 3:
265                 sys.stderr.write('"set" command requires 3 arguments\n')
266                 sys.exit(1)
267
268             s = idltest_find_simple(idl, int(args[0]))
269             if not s:
270                 sys.stderr.write('"set" command asks for nonexistent i=%d\n'
271                                  % int(args[0]))
272                 sys.exit(1)
273
274             if args[1] == "b":
275                 s.b = args[2] == "1"
276             elif args[1] == "s":
277                 s.s = args[2]
278             elif args[1] == "u":
279                 s.u = uuid.UUID(args[2])
280             elif args[1] == "r":
281                 s.r = float(args[2])
282             else:
283                 sys.stderr.write('"set" comamnd asks for unknown column %s\n'
284                                  % args[2])
285                 sys.stderr.exit(1)
286         elif name == "insert":
287             if len(args) != 1:
288                 sys.stderr.write('"set" command requires 1 argument\n')
289                 sys.exit(1)
290
291             s = txn.insert(idl.tables["simple"])
292             s.i = int(args[0])
293         elif name == "delete":
294             if len(args) != 1:
295                 sys.stderr.write('"delete" command requires 1 argument\n')
296                 sys.exit(1)
297
298             s = idltest_find_simple(idl, int(args[0]))
299             if not s:
300                 sys.stderr.write('"delete" command asks for nonexistent i=%d\n'
301                                  % int(args[0]))
302                 sys.exit(1)
303             s.delete()
304         elif name == "verify":
305             if len(args) != 2:
306                 sys.stderr.write('"verify" command requires 2 arguments\n')
307                 sys.exit(1)
308
309             s = idltest_find_simple(idl, int(args[0]))
310             if not s:
311                 sys.stderr.write('"verify" command asks for nonexistent i=%d\n'
312                                  % int(args[0]))
313                 sys.exit(1)
314
315             if args[1] in ("i", "b", "s", "u", "r"):
316                 s.verify(args[1])
317             else:
318                 sys.stderr.write('"verify" command asks for unknown column '
319                                  '"%s"\n' % args[1])
320                 sys.exit(1)
321         elif name == "fetch":
322             if len(args) != 2:
323                 sys.stderr.write('"fetch" command requires 2 argument\n')
324                 sys.exit(1)
325
326             row = idltest_find_simple(idl, int(args[0]))
327             if not row:
328                 sys.stderr.write('"fetch" command asks for nonexistent i=%d\n'
329                                  % int(args[0]))
330                 sys.exit(1)
331
332             column = args[1]
333             row.fetch(column)
334             fetch_cmds.append([row, column])
335         elif name == "increment":
336             if len(args) != 1:
337                 sys.stderr.write('"increment" command requires 1 argument\n')
338                 sys.exit(1)
339
340             s = idltest_find_simple(idl, int(args[0]))
341             if not s:
342                 sys.stderr.write('"set" command asks for nonexistent i=%d\n'
343                                  % int(args[0]))
344                 sys.exit(1)
345
346             s.increment("i")
347             increment = True
348         elif name == "abort":
349             txn.abort()
350             break
351         elif name == "destroy":
352             print "%03d: destroy" % step
353             sys.stdout.flush()
354             txn.abort()
355             return
356         elif name == "linktest":
357             l1_0 = txn.insert(idl.tables["link1"])
358             l1_0.i = 1
359             l1_0.k = [l1_0]
360             l1_0.ka = [l1_0]
361             l1_1 = txn.insert(idl.tables["link1"])
362             l1_1.i = 2
363             l1_1.k = [l1_0]
364             l1_1.ka = [l1_0, l1_1]
365         elif name == 'getattrtest':
366             l1 = txn.insert(idl.tables["link1"])
367             i = getattr(l1, 'i', 1)
368             assert i == 1
369             l1.i = 2
370             i = getattr(l1, 'i', 1)
371             assert i == 2
372             l1.k = [l1]
373         else:
374             sys.stderr.write("unknown command %s\n" % name)
375             sys.exit(1)
376
377     status = txn.commit_block()
378     sys.stdout.write("%03d: commit, status=%s"
379                      % (step, ovs.db.idl.Transaction.status_to_string(status)))
380     if increment and status == ovs.db.idl.Transaction.SUCCESS:
381         sys.stdout.write(", increment=%d" % txn.get_increment_new_value())
382     if events:
383         # Event notifications from operations in a single transaction are
384         # not in a gauranteed order due to update messages being dicts
385         sys.stdout.write(", events=" + ", ".join(sorted(events)))
386     sys.stdout.write("\n")
387     sys.stdout.flush()
388
389
390 def do_idl(schema_file, remote, *commands):
391     schema_helper = ovs.db.idl.SchemaHelper(schema_file)
392     if commands and commands[0].startswith("?"):
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 command_name not 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)