]>
git.proxmox.com Git - ovs.git/blob - tests/test-ovsdb.py
1 # Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
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:
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 from __future__
import print_function
23 from ovs
.db
import error
26 from ovs
.db
import data
31 from ovs
.fatal_signal
import signal_alarm
36 if type(json
) == list and len(json
) == 1:
42 def do_default_atoms():
43 for type_
in ovs
.db
.types
.ATOMIC_TYPES
:
44 if type_
== ovs
.db
.types
.VoidType
:
47 sys
.stdout
.write("%s: " % type_
.to_string())
49 atom
= data
.Atom
.default(type_
)
50 if atom
!= data
.Atom
.default(type_
):
51 sys
.stdout
.write("wrong\n")
54 sys
.stdout
.write("OK\n")
57 def do_default_data():
60 for key
in ovs
.db
.types
.ATOMIC_TYPES
:
61 if key
== ovs
.db
.types
.VoidType
:
63 for value
in ovs
.db
.types
.ATOMIC_TYPES
:
64 if value
== ovs
.db
.types
.VoidType
:
67 valueBase
= ovs
.db
.types
.BaseType(value
)
68 type_
= ovs
.db
.types
.Type(ovs
.db
.types
.BaseType(key
),
70 assert type_
.is_valid()
72 sys
.stdout
.write("key %s, value %s, n_min %d: "
73 % (key
.to_string(), value
.to_string(), n_min
))
75 datum
= data
.Datum
.default(type_
)
76 if datum
!= data
.Datum
.default(type_
):
77 sys
.stdout
.write("wrong\n")
80 sys
.stdout
.write("OK\n")
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))
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))
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))
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
))
109 atom
= data
.Atom
.from_json(base
, atom_json
)
110 print(ovs
.json
.to_string(atom
.to_json()))
111 except error
.Error
as e
:
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()))
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
)]))
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))
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))
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))
152 def get_simple_printable_row_string(row
, columns
):
154 for column
in columns
:
155 if hasattr(row
, column
) and not (type(getattr(row
, column
))
156 is ovs
.db
.data
.Atom
):
157 value
= getattr(row
, column
)
158 if isinstance(value
, dict):
159 value
= sorted(value
.items())
160 s
+= "%s=%s " % (column
, value
)
162 s
= re
.sub('""|,|u?\'', "", s
)
163 s
= re
.sub('UUID\(([^)]+)\)', r
'\1', s
)
164 s
= re
.sub('False', 'false', s
)
165 s
= re
.sub('True', 'true', s
)
166 s
= re
.sub(r
'(ba)=([^[][^ ]*) ', r
'\1=[\2] ', s
)
170 def get_simple_table_printable_row(row
):
171 simple_columns
= ["i", "r", "b", "s", "u", "ia",
172 "ra", "ba", "sa", "ua", "uuid"]
173 return get_simple_printable_row_string(row
, simple_columns
)
176 def get_simple2_table_printable_row(row
):
177 simple2_columns
= ["name", "smap", "imap"]
178 return get_simple_printable_row_string(row
, simple2_columns
)
181 def get_simple3_table_printable_row(row
):
182 simple3_columns
= ["name", "uset"]
183 return get_simple_printable_row_string(row
, simple3_columns
)
186 def print_idl(idl
, step
):
188 if "simple" in idl
.tables
:
189 simple
= idl
.tables
["simple"].rows
190 for row
in six
.itervalues(simple
):
192 s
+= get_simple_table_printable_row(row
)
196 if "simple2" in idl
.tables
:
197 simple2
= idl
.tables
["simple2"].rows
198 for row
in six
.itervalues(simple2
):
200 s
+= get_simple2_table_printable_row(row
)
204 if "simple3" in idl
.tables
:
205 simple3
= idl
.tables
["simple3"].rows
206 for row
in six
.itervalues(simple3
):
208 s
+= get_simple3_table_printable_row(row
)
212 if "link1" in idl
.tables
:
213 l1
= idl
.tables
["link1"].rows
214 for row
in six
.itervalues(l1
):
215 s
= ["%03d: i=%s k=" % (step
, row
.i
)]
216 if hasattr(row
, "k") and row
.k
:
217 s
.append(str(row
.k
.i
))
218 if hasattr(row
, "ka"):
220 s
.append(' '.join(sorted(str(ka
.i
) for ka
in row
.ka
)))
222 if hasattr(row
, "l2") and row
.l2
:
223 s
.append(str(row
.l2
[0].i
))
224 if hasattr(row
, "uuid"):
225 s
.append(" uuid=%s" % row
.uuid
)
229 if "link2" in idl
.tables
:
230 l2
= idl
.tables
["link2"].rows
231 for row
in six
.itervalues(l2
):
233 s
.append(" i=%s l1=" % row
.i
)
234 if hasattr(row
, "l1") and row
.l1
:
235 s
.append(str(row
.l1
[0].i
))
236 if hasattr(row
, "uuid"):
237 s
.append(" uuid=%s" % row
.uuid
)
242 print("%03d: empty" % step
)
246 def substitute_uuids(json
, symtab
):
247 if isinstance(json
, six
.string_types
):
248 symbol
= symtab
.get(json
)
251 elif type(json
) == list:
252 return [substitute_uuids(element
, symtab
) for element
in json
]
253 elif type(json
) == dict:
255 for key
, value
in six
.iteritems(json
):
256 d
[key
] = substitute_uuids(value
, symtab
)
261 def parse_uuids(json
, symtab
):
262 if (isinstance(json
, six
.string_types
)
263 and ovs
.ovsuuid
.is_valid_string(json
)):
264 name
= "#%d#" % len(symtab
)
265 sys
.stderr
.write("%s = %s\n" % (name
, json
))
267 elif type(json
) == list:
269 parse_uuids(element
, symtab
)
270 elif type(json
) == dict:
271 for value
in six
.itervalues(json
):
272 parse_uuids(value
, symtab
)
275 def idltest_find_simple(idl
, i
):
276 for row
in six
.itervalues(idl
.tables
["simple"].rows
):
282 def idltest_find_simple2(idl
, i
):
283 for row
in six
.itervalues(idl
.tables
["simple2"].rows
):
289 def idltest_find_simple3(idl
, i
):
290 for row
in six
.itervalues(idl
.tables
["simple3"].rows
):
296 def idl_set(idl
, commands
, step
):
297 txn
= ovs
.db
.idl
.Transaction(idl
)
301 for command
in commands
.split(','):
302 words
= command
.split()
306 if name
== "notifytest":
309 old_notify
= idl
.notify
311 def notify(event
, row
, updates
=None):
313 upcol
= list(updates
._data
.keys())[0]
316 events
.append("%s|%s|%s" % (event
, row
.i
, upcol
))
317 idl
.notify
= old_notify
323 sys
.stderr
.write('"set" command requires 3 arguments\n')
326 s
= idltest_find_simple(idl
, int(args
[0]))
328 sys
.stderr
.write('"set" command asks for nonexistent i=%d\n'
337 s
.u
= uuid
.UUID(args
[2])
341 sys
.stderr
.write('"set" comamnd asks for unknown column %s\n'
344 elif name
== "insert":
346 sys
.stderr
.write('"set" command requires 1 argument\n')
349 s
= txn
.insert(idl
.tables
["simple"])
351 elif name
== "delete":
353 sys
.stderr
.write('"delete" command requires 1 argument\n')
356 s
= idltest_find_simple(idl
, int(args
[0]))
358 sys
.stderr
.write('"delete" command asks for nonexistent i=%d\n'
362 elif name
== "verify":
364 sys
.stderr
.write('"verify" command requires 2 arguments\n')
367 s
= idltest_find_simple(idl
, int(args
[0]))
369 sys
.stderr
.write('"verify" command asks for nonexistent i=%d\n'
373 if args
[1] in ("i", "b", "s", "u", "r"):
376 sys
.stderr
.write('"verify" command asks for unknown column '
379 elif name
== "fetch":
381 sys
.stderr
.write('"fetch" command requires 2 argument\n')
384 row
= idltest_find_simple(idl
, int(args
[0]))
386 sys
.stderr
.write('"fetch" command asks for nonexistent i=%d\n'
392 fetch_cmds
.append([row
, column
])
393 elif name
== "increment":
395 sys
.stderr
.write('"increment" command requires 1 argument\n')
398 s
= idltest_find_simple(idl
, int(args
[0]))
400 sys
.stderr
.write('"set" command asks for nonexistent i=%d\n'
406 elif name
== "abort":
409 elif name
== "destroy":
410 print("%03d: destroy" % step
)
414 elif name
== "linktest":
415 l1_0
= txn
.insert(idl
.tables
["link1"])
419 l1_1
= txn
.insert(idl
.tables
["link1"])
422 l1_1
.ka
= [l1_0
, l1_1
]
423 elif name
== 'getattrtest':
424 l1
= txn
.insert(idl
.tables
["link1"])
425 i
= getattr(l1
, 'i', 1)
428 i
= getattr(l1
, 'i', 1)
431 elif name
== 'partialmapinsertelement':
432 row
= idltest_find_simple2(idl
, 'myString1')
433 row
.setkey('smap', 'key1', 'myList1')
434 row
.setkey('imap', 3, 'myids2')
435 row
.__setattr
__('name', 'String2')
436 elif name
== 'partialmapinsertmultipleelements':
437 row
= idltest_find_simple2(idl
, 'String2')
438 row
.setkey('smap', 'key2', 'myList2')
439 row
.setkey('smap', 'key3', 'myList3')
440 elif name
== 'partialmapdelelements':
441 row
= idltest_find_simple2(idl
, 'String2')
442 row
.delkey('smap', 'key1', 'myList1')
443 row
.delkey('smap', 'key2', 'wrongvalue')
444 row
.delkey('smap', 'key3')
445 elif name
== 'partialrenamesetadd':
446 row
= idltest_find_simple3(idl
, 'mySet1')
448 uuid
.UUID("001e43d2-dd3f-4616-ab6a-83a490bb0991"))
449 row
.__setattr
__('name', 'String2')
450 elif name
== 'partialduplicateadd':
451 row
= idltest_find_simple3(idl
, 'String2')
453 uuid
.UUID("0026b3ba-571b-4729-8227-d860a5210ab8"))
455 uuid
.UUID("0026b3ba-571b-4729-8227-d860a5210ab8"))
456 elif name
== 'partialsetdel':
457 row
= idltest_find_simple3(idl
, 'String2')
459 uuid
.UUID("001e43d2-dd3f-4616-ab6a-83a490bb0991"))
460 elif name
== 'partialsetref':
461 new_row
= txn
.insert(idl
.tables
["simple4"])
462 new_row
.__setattr
__('name', 'test')
463 row
= idltest_find_simple3(idl
, 'String2')
464 row
.addvalue('uref', new_row
.uuid
)
465 elif name
== 'partialsetoverrideops':
466 row
= idltest_find_simple3(idl
, 'String2')
468 uuid
.UUID("579e978d-776c-4f19-a225-268e5890e670"))
470 uuid
.UUID("0026b3ba-571b-4729-8227-d860a5210ab8"))
471 row
.__setattr
__('uset',
472 [uuid
.UUID("0026b3ba-571b-4729-8227-d860a5210ab8")])
473 elif name
== 'partialsetmutatenew':
474 new_row41
= txn
.insert(idl
.tables
["simple4"])
475 new_row41
.__setattr__('name', 'new_row41')
476 new_row3
= txn
.insert(idl
.tables
["simple3"])
477 setattr(new_row3
, 'name', 'String3')
478 new_row3
.addvalue('uset', new_row41
.uuid
)
480 sys
.stderr
.write("unknown command %s\n" % name
)
483 status
= txn
.commit_block()
484 sys
.stdout
.write("%03d: commit, status=%s"
485 % (step
, ovs
.db
.idl
.Transaction
.status_to_string(status
)))
486 if increment
and status
== ovs
.db
.idl
.Transaction
.SUCCESS
:
487 sys
.stdout
.write(", increment=%d" % txn
.get_increment_new_value())
489 # Event notifications from operations in a single transaction are
490 # not in a gauranteed order due to update messages being dicts
491 sys
.stdout
.write(", events=" + ", ".join(sorted(events
)))
492 sys
.stdout
.write("\n")
496 def update_condition(idl
, commands
):
497 commands
= commands
.split(";")
498 for command
in commands
:
499 command
= command
[len("condition "):]
502 command
= command
[len("add "):]
505 command
= command
[len("remove "):]
507 command
= command
.split(" ")
508 if(len(command
) != 2):
509 sys
.stderr
.write("Error parsong condition %s\n" % command
)
513 cond
= ovs
.json
.from_string(command
[1])
515 idl
.cond_change(table
, add_cmd
, cond
)
518 def do_idl(schema_file
, remote
, *commands
):
519 schema_helper
= ovs
.db
.idl
.SchemaHelper(schema_file
)
522 if commands
and commands
[0] == "track-notify":
523 commands
= commands
[1:]
526 if commands
and commands
[0].startswith("?"):
528 for x
in commands
[0][1:].split("?"):
530 table
, columns
= x
.split(":")
531 columns
= columns
.split(",")
532 for index
, column
in enumerate(columns
):
533 if column
[-1] == '!':
534 columns
[index
] = columns
[index
][:-1]
535 readonly
.append(columns
[index
])
536 schema_helper
.register_columns(table
, columns
, readonly
)
537 commands
= commands
[1:]
539 schema_helper
.register_all()
540 idl
= ovs
.db
.idl
.Idl(remote
, schema_helper
)
543 error
, stream
= ovs
.stream
.Stream
.open_block(
544 ovs
.stream
.Stream
.open(remote
))
546 sys
.stderr
.write("failed to connect to \"%s\"" % remote
)
548 rpc
= ovs
.jsonrpc
.Connection(stream
)
556 def mock_notify(event
, row
, updates
=None):
557 output
= "%03d: " % step
558 output
+= "event:" + str(event
) + ", row={"
559 output
+= get_simple_table_printable_row(row
) + "}, updates="
563 output
+= "{" + get_simple_table_printable_row(updates
) + "}"
566 sys
.stdout
.write(output
)
569 if track_notify
and "simple" in idl
.tables
:
570 idl
.notify
= mock_notify
572 commands
= list(commands
)
573 if len(commands
) >= 1 and "condition" in commands
[0]:
574 update_condition(idl
, commands
.pop(0))
575 sys
.stdout
.write("%03d: change conditions\n" % step
)
579 for command
in commands
:
580 if command
.startswith("+"):
581 # The previous transaction didn't change anything.
582 command
= command
[1:]
585 while idl
.change_seqno
== seqno
and not idl
.run():
588 poller
= ovs
.poller
.Poller()
596 seqno
= idl
.change_seqno
598 if command
== "reconnect":
599 print("%03d: reconnect" % step
)
602 idl
.force_reconnect()
603 elif "condition" in command
:
604 update_condition(idl
, command
)
605 sys
.stdout
.write("%03d: change conditions\n" % step
)
608 elif not command
.startswith("["):
609 idl_set(idl
, command
, step
)
612 json
= ovs
.json
.from_string(command
)
613 if isinstance(json
, six
.string_types
):
614 sys
.stderr
.write("\"%s\": %s\n" % (command
, json
))
616 json
= substitute_uuids(json
, symtab
)
617 request
= ovs
.jsonrpc
.Message
.create_request("transact", json
)
618 error
, reply
= rpc
.transact_block(request
)
620 sys
.stderr
.write("jsonrpc transaction failed: %s"
621 % os
.strerror(error
))
623 elif reply
.error
is not None:
624 sys
.stderr
.write("jsonrpc transaction failed: %s"
628 sys
.stdout
.write("%03d: " % step
)
631 if reply
.result
is not None:
632 parse_uuids(reply
.result
, symtab
)
634 sys
.stdout
.write("%s\n" % ovs
.json
.to_string(reply
.to_json()))
639 while idl
.change_seqno
== seqno
and not idl
.run():
640 poller
= ovs
.poller
.Poller()
646 print("%03d: done" % step
)
649 def do_idl_passive(schema_file
, remote
, *commands
):
652 schema_helper
= ovs
.db
.idl
.SchemaHelper(schema_file
)
653 schema_helper
.register_all()
654 idl
= ovs
.db
.idl
.Idl(remote
, schema_helper
)
656 while idl
._session
.rpc
is None:
659 rpc
= idl
._session
.rpc
664 for command
in commands
:
665 json
= ovs
.json
.from_string(command
)
666 if isinstance(json
, six
.string_types
):
667 sys
.stderr
.write("\"%s\": %s\n" % (command
, json
))
669 json
= substitute_uuids(json
, symtab
)
670 request
= ovs
.jsonrpc
.Message
.create_request("transact", json
)
671 error
, reply
= rpc
.transact_block(request
)
673 sys
.stderr
.write("jsonrpc transaction failed: %s"
674 % os
.strerror(error
))
676 elif reply
.error
is not None:
677 sys
.stderr
.write("jsonrpc transaction failed: %s"
681 sys
.stdout
.write("%03d: " % step
)
684 if reply
.result
is not None:
685 parse_uuids(reply
.result
, symtab
)
687 sys
.stdout
.write("%s\n" % ovs
.json
.to_string(reply
.to_json()))
691 print("%03d: done" % step
)
696 %(program_name)s: test utility for Open vSwitch database Python bindings
697 usage: %(program_name)s [OPTIONS] COMMAND ARG...
699 The following commands are supported:
701 test ovsdb_atom_default()
703 test ovsdb_datum_default()
704 parse-atomic-type TYPE
705 parse TYPE as OVSDB atomic type, and re-serialize
707 parse TYPE as OVSDB base type, and re-serialize
709 parse JSON as OVSDB type, and re-serialize
710 parse-atoms TYPE ATOM...
711 parse JSON ATOMs as atoms of TYPE, and re-serialize
712 parse-atom-strings TYPE ATOM...
713 parse string ATOMs as atoms of given TYPE, and re-serialize
714 sort-atoms TYPE ATOM...
715 print JSON ATOMs in sorted order
716 parse-data TYPE DATUM...
717 parse JSON DATUMs as data of given TYPE, and re-serialize
718 parse-column NAME OBJECT
719 parse column NAME with info OBJECT, and re-serialize
720 parse-table NAME OBJECT [DEFAULT-IS-ROOT]
721 parse table NAME with info OBJECT
723 parse JSON as an OVSDB schema, and re-serialize
724 idl SCHEMA SERVER [?T1:C1,C2...[?T2:C1,C2,...]...] [TRANSACTION...]
725 connect to SERVER (which has the specified SCHEMA) and dump the
726 contents of the database as seen initially by the IDL implementation
727 and after executing each TRANSACTION. (Each TRANSACTION must modify
728 the database or this command will hang.)
729 By default, all columns of all tables are monitored. The "?" option
730 can be used to monitor specific Table:Column(s). The table and their
731 columns are listed as a string of the form starting with "?":
732 ?<table-name>:<column-name>,<column-name>,...
734 ?simple:b - Monitor column "b" in table "simple"
735 Entries for multiple tables are seperated by "?":
736 ?<table-name>:<column-name>,...?<table-name>:<column-name>,...
738 ?simple:b?link1:i,k - Monitor column "b" in table "simple",
739 and column "i", "k" in table "link1"
740 Readonly columns: Suffixing a "!" after a column indicates that the
741 column is to be registered "readonly".
743 ?simple:i,b! - Register interest in column "i" (monitoring) and
744 column "b" (readonly).
747 The following options are also available:
748 -t, --timeout=SECS give up after SECS seconds
749 -h, --help display this help message\
750 """ % {'program_name': ovs
.util
.PROGRAM_NAME
})
756 options
, args
= getopt
.gnu_getopt(argv
[1:], 't:h',
759 except getopt
.GetoptError
as geo
:
760 sys
.stderr
.write("%s: %s\n" % (ovs
.util
.PROGRAM_NAME
, geo
.msg
))
763 for key
, value
in options
:
764 if key
in ['-h', '--help']:
766 elif key
in ['-t', '--timeout']:
772 raise error
.Error("value %s on -t or --timeout is not at "
774 signal_alarm(timeout
)
779 sys
.stderr
.write("%s: missing command argument "
780 "(use --help for help)\n" % ovs
.util
.PROGRAM_NAME
)
783 commands
= {"default-atoms": (do_default_atoms
, 0),
784 "default-data": (do_default_data
, 0),
785 "parse-atomic-type": (do_parse_atomic_type
, 1),
786 "parse-base-type": (do_parse_base_type
, 1),
787 "parse-type": (do_parse_type
, 1),
788 "parse-atoms": (do_parse_atoms
, (2,)),
789 "parse-data": (do_parse_data
, (2,)),
790 "sort-atoms": (do_sort_atoms
, 2),
791 "parse-column": (do_parse_column
, 2),
792 "parse-table": (do_parse_table
, (2, 3)),
793 "parse-schema": (do_parse_schema
, 1),
794 "idl": (do_idl
, (2,)),
795 "idl_passive": (do_idl_passive
, (2,))}
797 command_name
= args
[0]
799 if command_name
not in commands
:
800 sys
.stderr
.write("%s: unknown command \"%s\" "
801 "(use --help for help)\n" % (ovs
.util
.PROGRAM_NAME
,
805 func
, n_args
= commands
[command_name
]
806 if type(n_args
) == tuple:
807 if len(args
) < n_args
[0]:
808 sys
.stderr
.write("%s: \"%s\" requires at least %d arguments but "
810 % (ovs
.util
.PROGRAM_NAME
, command_name
,
813 elif type(n_args
) == int:
814 if len(args
) != n_args
:
815 sys
.stderr
.write("%s: \"%s\" requires %d arguments but %d "
817 % (ovs
.util
.PROGRAM_NAME
, command_name
,
826 if __name__
== '__main__':
829 except error
.Error
as e
:
830 sys
.stderr
.write("%s\n" % e
)