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