ovsdb: Implement garbage collection.
[cascardo/ovs.git] / tests / test-ovsdb.py
1 # Copyright (c) 2009, 2010, 2011 Nicira Networks
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 codecs
16 import getopt
17 import re
18 import os
19 import signal
20 import sys
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 def unbox_json(json):
32     if type(json) == list and len(json) == 1:
33         return json[0]
34     else:
35         return json
36
37 def do_default_atoms():
38     for type in types.ATOMIC_TYPES:
39         if type == types.VoidType:
40             continue
41
42         sys.stdout.write("%s: " % type.to_string())
43
44         atom = data.Atom.default(type)
45         if atom != data.Atom.default(type):
46             sys.stdout.write("wrong\n")
47             sys.exit(1)
48
49         sys.stdout.write("OK\n")
50
51 def do_default_data():
52     any_errors = False
53     for n_min in 0, 1:
54         for key in types.ATOMIC_TYPES:
55             if key == types.VoidType:
56                 continue
57             for value in types.ATOMIC_TYPES:
58                 if value == types.VoidType:
59                     valueBase = None
60                 else:
61                     valueBase = types.BaseType(value)
62                 type = types.Type(types.BaseType(key), valueBase, n_min, 1)
63                 assert type.is_valid()
64
65                 sys.stdout.write("key %s, value %s, n_min %d: "
66                                  % (key.to_string(), value.to_string(), n_min))
67
68                 datum = data.Datum.default(type)
69                 if datum != data.Datum.default(type):
70                     sys.stdout.write("wrong\n")
71                     any_errors = True
72                 else:
73                     sys.stdout.write("OK\n")
74     if any_errors:
75         sys.exit(1)
76
77 def do_parse_atomic_type(type_string):
78     type_json = unbox_json(ovs.json.from_string(type_string))
79     atomic_type = types.AtomicType.from_json(type_json)
80     print ovs.json.to_string(atomic_type.to_json(), sort_keys=True)
81
82 def do_parse_base_type(type_string):
83     type_json = unbox_json(ovs.json.from_string(type_string))
84     base_type = types.BaseType.from_json(type_json)
85     print ovs.json.to_string(base_type.to_json(), sort_keys=True)
86
87 def do_parse_type(type_string):
88     type_json = unbox_json(ovs.json.from_string(type_string))
89     type = types.Type.from_json(type_json)
90     print ovs.json.to_string(type.to_json(), sort_keys=True)
91
92 def do_parse_atoms(type_string, *atom_strings):
93     type_json = unbox_json(ovs.json.from_string(type_string))
94     base = types.BaseType.from_json(type_json)
95     for atom_string in atom_strings:
96         atom_json = unbox_json(ovs.json.from_string(atom_string))
97         try:
98             atom = data.Atom.from_json(base, atom_json)
99             print ovs.json.to_string(atom.to_json())
100         except error.Error, e:
101             print e
102
103 def do_parse_data(type_string, *data_strings):
104     type_json = unbox_json(ovs.json.from_string(type_string))
105     type = types.Type.from_json(type_json)
106     for datum_string in data_strings:
107         datum_json = unbox_json(ovs.json.from_string(datum_string))
108         datum = data.Datum.from_json(type, datum_json)
109         print ovs.json.to_string(datum.to_json())
110
111 def do_sort_atoms(type_string, atom_strings):
112     type_json = unbox_json(ovs.json.from_string(type_string))
113     base = types.BaseType.from_json(type_json)
114     atoms = [data.Atom.from_json(base, atom_json)
115              for atom_json in unbox_json(ovs.json.from_string(atom_strings))]
116     print ovs.json.to_string([data.Atom.to_json(atom)
117                               for atom in sorted(atoms)])
118
119 def do_parse_column(name, column_string):
120     column_json = unbox_json(ovs.json.from_string(column_string))
121     column = ovs.db.schema.ColumnSchema.from_json(column_json, name)
122     print ovs.json.to_string(column.to_json(), sort_keys=True)
123
124 def do_parse_table(name, table_string, default_is_root_string='false'):
125     default_is_root = default_is_root_string == 'true'
126     table_json = unbox_json(ovs.json.from_string(table_string))
127     table = ovs.db.schema.TableSchema.from_json(table_json, name)
128     print ovs.json.to_string(table.to_json(default_is_root), sort_keys=True)
129
130 def do_parse_rows(table_string, *rows):
131     table_json = unbox_json(ovs.json.from_string(table_string))
132     table = ovs.db.schema.TableSchema.from_json(table_json, name)
133
134 def do_parse_schema(schema_string):
135     schema_json = unbox_json(ovs.json.from_string(schema_string))
136     schema = ovs.db.schema.DbSchema.from_json(schema_json)
137     print ovs.json.to_string(schema.to_json(), sort_keys=True)
138
139 def print_idl(idl, step):
140     n = 0
141     for uuid, row in idl.data["simple"].iteritems():
142         s = ("%03d: i=%s r=%s b=%s s=%s u=%s "
143              "ia=%s ra=%s ba=%s sa=%s ua=%s uuid=%s"
144              % (step, row.i, row.r, row.b, row.s, row.u,
145                 row.ia, row.ra, row.ba, row.sa, row.ua, uuid))
146         print(re.sub('""|,', "", s))
147         n += 1
148     if not n:
149         print("%03d: empty" % step)
150
151 def substitute_uuids(json, symtab):
152     if type(json) in [str, unicode]:
153         symbol = symtab.get(json)
154         if symbol:
155             return str(symbol)
156     elif type(json) == list:
157         return [substitute_uuids(element, symtab) for element in json]
158     elif type(json) == dict:
159         d = {}
160         for key, value in json.iteritems():
161             d[key] = substitute_uuids(value, symtab)
162         return d
163     return json
164
165 def parse_uuids(json, symtab):
166     if type(json) in [str, unicode] and ovs.ovsuuid.UUID.is_valid_string(json):
167         name = "#%d#" % len(symtab)
168         sys.stderr.write("%s = %s\n" % (name, json))
169         symtab[name] = json
170     elif type(json) == list:
171         for element in json:
172             parse_uuids(element, symtab)
173     elif type(json) == dict:
174         for value in json.itervalues():
175             parse_uuids(value, symtab)
176
177 def do_idl(remote, *commands):
178     idl = ovs.db.idl.Idl(remote, "idltest")
179
180     if commands:
181         error, stream = ovs.stream.Stream.open_block(
182             ovs.stream.Stream.open(remote))
183         if error:
184             sys.stderr.write("failed to connect to \"%s\"" % remote)
185             sys.exit(1)
186         rpc = ovs.jsonrpc.Connection(stream)
187     else:
188         rpc = None
189
190     symtab = {}
191     seqno = 0
192     step = 0
193     for command in commands:
194         if command.startswith("+"):
195             # The previous transaction didn't change anything.
196             command = command[1:]
197         else:
198             # Wait for update.
199             while idl.get_seqno() == seqno and not idl.run():
200                 rpc.run()
201
202                 poller = ovs.poller.Poller()
203                 idl.wait(poller)
204                 rpc.wait(poller)
205                 poller.block()
206                 
207             print_idl(idl, step)
208             step += 1
209
210         seqno = idl.get_seqno()
211
212         if command == "reconnect":
213             print("%03d: reconnect" % step)
214             step += 1
215             idl.force_reconnect()
216         elif not command.startswith("["):
217             idl_set(idl, command, step)
218             step += 1
219         else:
220             json = ovs.json.from_string(command)
221             if type(json) in [str, unicode]:
222                 sys.stderr.write("\"%s\": %s\n" % (command, json))
223                 sys.exit(1)
224             json = substitute_uuids(json, symtab)
225             request = ovs.jsonrpc.Message.create_request("transact", json)
226             error, reply = rpc.transact_block(request)
227             if error:
228                 sys.stderr.write("jsonrpc transaction failed: %s"
229                                  % os.strerror(error))
230                 sys.exit(1)
231             sys.stdout.write("%03d: " % step)
232             sys.stdout.flush()
233             step += 1
234             if reply.result is not None:
235                 parse_uuids(reply.result, symtab)
236             reply.id = None
237             sys.stdout.write("%s\n" % ovs.json.to_string(reply.to_json()))
238
239     if rpc:
240         rpc.close()
241     while idl.get_seqno() == seqno and not idl.run():
242         poller = ovs.poller.Poller()
243         idl.wait(poller)
244         poller.block()
245     print_idl(idl, step)
246     step += 1
247     idl.close()
248     print("%03d: done" % step)
249
250 def usage():
251     print """\
252 %(program_name)s: test utility for Open vSwitch database Python bindings
253 usage: %(program_name)s [OPTIONS] COMMAND ARG...
254
255 The following commands are supported:
256 default-atoms
257   test ovsdb_atom_default()
258 default-data
259   test ovsdb_datum_default()
260 parse-atomic-type TYPE
261   parse TYPE as OVSDB atomic type, and re-serialize
262 parse-base-type TYPE
263   parse TYPE as OVSDB base type, and re-serialize
264 parse-type JSON
265   parse JSON as OVSDB type, and re-serialize
266 parse-atoms TYPE ATOM...
267   parse JSON ATOMs as atoms of TYPE, and re-serialize
268 parse-atom-strings TYPE ATOM...
269   parse string ATOMs as atoms of given TYPE, and re-serialize
270 sort-atoms TYPE ATOM...
271   print JSON ATOMs in sorted order
272 parse-data TYPE DATUM...
273   parse JSON DATUMs as data of given TYPE, and re-serialize
274 parse-column NAME OBJECT
275   parse column NAME with info OBJECT, and re-serialize
276 parse-table NAME OBJECT [DEFAULT-IS-ROOT]
277   parse table NAME with info OBJECT
278 parse-schema JSON
279   parse JSON as an OVSDB schema, and re-serialize
280 idl SERVER [TRANSACTION...]
281   connect to SERVER and dump the contents of the database
282   as seen initially by the IDL implementation and after
283   executing each TRANSACTION.  (Each TRANSACTION must modify
284   the database or this command will hang.)
285
286 The following options are also available:
287   -t, --timeout=SECS          give up after SECS seconds
288   -h, --help                  display this help message\
289 """ % {'program_name': ovs.util.PROGRAM_NAME}
290     sys.exit(0)
291
292 def main(argv):
293     # Make stdout and stderr UTF-8, even if they are redirected to a file.
294     sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
295     sys.stderr = codecs.getwriter("utf-8")(sys.stderr)
296
297     try:
298         options, args = getopt.gnu_getopt(argv[1:], 't:h',
299                                           ['timeout',
300                                            'help'])
301     except getopt.GetoptError, geo:
302         sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg))
303         sys.exit(1)
304
305     for key, value in options:
306         if key in ['-h', '--help']:
307             usage()
308         elif key in ['-t', '--timeout']:
309             try:
310                 timeout = int(value)
311                 if timeout < 1:
312                     raise TypeError
313             except TypeError:
314                 raise error.Error("value %s on -t or --timeout is not at "
315                                   "least 1" % value)
316             signal.alarm(timeout)
317         else:
318             sys.exit(0)
319
320     optKeys = [key for key, value in options]
321
322     if not args:
323         sys.stderr.write("%s: missing command argument "
324                          "(use --help for help)\n" % ovs.util.PROGRAM_NAME)
325         sys.exit(1)
326
327     commands = {"default-atoms": (do_default_atoms, 0),
328                 "default-data": (do_default_data, 0),
329                 "parse-atomic-type": (do_parse_atomic_type, 1),
330                 "parse-base-type": (do_parse_base_type, 1),
331                 "parse-type": (do_parse_type, 1),
332                 "parse-atoms": (do_parse_atoms, (2,)),
333                 "parse-data": (do_parse_data, (2,)),
334                 "sort-atoms": (do_sort_atoms, 2),
335                 "parse-column": (do_parse_column, 2),
336                 "parse-table": (do_parse_table, (2, 3)),
337                 "parse-schema": (do_parse_schema, 1),
338                 "idl": (do_idl, (1,))}
339
340     command_name = args[0]
341     args = args[1:]
342     if not command_name in commands:
343         sys.stderr.write("%s: unknown command \"%s\" "
344                          "(use --help for help)\n" % (ovs.util.PROGRAM_NAME,
345                                                       command_name))
346         sys.exit(1)
347
348     func, n_args = commands[command_name]
349     if type(n_args) == tuple:
350         if len(args) < n_args[0]:
351             sys.stderr.write("%s: \"%s\" requires at least %d arguments but "
352                              "only %d provided\n"
353                              % (ovs.util.PROGRAM_NAME, command_name,
354                                 n_args, len(args)))
355             sys.exit(1)
356     elif type(n_args) == int:
357         if len(args) != n_args:
358             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
359                              "provided\n"
360                              % (ovs.util.PROGRAM_NAME, command_name,
361                                 n_args, len(args)))
362             sys.exit(1)
363     else:
364         assert False
365
366     func(*args)
367
368 if __name__ == '__main__':
369     try:
370         main(sys.argv)
371     except error.Error, e:
372         sys.stderr.write("%s\n" % e)
373         sys.exit(1)