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