Package ganeti :: Package client :: Module gnt_debug
[hide private]
[frames] | no frames]

Source Code for Module ganeti.client.gnt_debug

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2010, 2011 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21  """Debugging commands""" 
 22   
 23  # pylint: disable=W0401,W0614,C0103 
 24  # W0401: Wildcard import ganeti.cli 
 25  # W0614: Unused import %s from wildcard import (since we need cli) 
 26  # C0103: Invalid name gnt-backup 
 27   
 28  import simplejson 
 29  import time 
 30  import socket 
 31  import logging 
 32   
 33  from ganeti.cli import * 
 34  from ganeti import cli 
 35  from ganeti import constants 
 36  from ganeti import opcodes 
 37  from ganeti import utils 
 38  from ganeti import errors 
 39  from ganeti import compat 
 40  from ganeti import ht 
 41   
 42   
 43  #: Default fields for L{ListLocks} 
 44  _LIST_LOCKS_DEF_FIELDS = [ 
 45    "name", 
 46    "mode", 
 47    "owner", 
 48    "pending", 
 49    ] 
 50   
 51   
52 -def Delay(opts, args):
53 """Sleeps for a while 54 55 @param opts: the command line options selected by the user 56 @type args: list 57 @param args: should contain only one element, the duration 58 the sleep 59 @rtype: int 60 @return: the desired exit code 61 62 """ 63 delay = float(args[0]) 64 op = opcodes.OpTestDelay(duration=delay, 65 on_master=opts.on_master, 66 on_nodes=opts.on_nodes, 67 repeat=opts.repeat) 68 SubmitOpCode(op, opts=opts) 69 70 return 0
71 72
73 -def GenericOpCodes(opts, args):
74 """Send any opcode to the master. 75 76 @param opts: the command line options selected by the user 77 @type args: list 78 @param args: should contain only one element, the path of 79 the file with the opcode definition 80 @rtype: int 81 @return: the desired exit code 82 83 """ 84 cl = cli.GetClient() 85 jex = cli.JobExecutor(cl=cl, verbose=opts.verbose, opts=opts) 86 87 job_cnt = 0 88 op_cnt = 0 89 if opts.timing_stats: 90 ToStdout("Loading...") 91 for job_idx in range(opts.rep_job): 92 for fname in args: 93 # pylint: disable=W0142 94 op_data = simplejson.loads(utils.ReadFile(fname)) 95 op_list = [opcodes.OpCode.LoadOpCode(val) for val in op_data] 96 op_list = op_list * opts.rep_op 97 jex.QueueJob("file %s/%d" % (fname, job_idx), *op_list) 98 op_cnt += len(op_list) 99 job_cnt += 1 100 101 if opts.timing_stats: 102 t1 = time.time() 103 ToStdout("Submitting...") 104 105 jex.SubmitPending(each=opts.each) 106 107 if opts.timing_stats: 108 t2 = time.time() 109 ToStdout("Executing...") 110 111 jex.GetResults() 112 if opts.timing_stats: 113 t3 = time.time() 114 ToStdout("C:op %4d" % op_cnt) 115 ToStdout("C:job %4d" % job_cnt) 116 ToStdout("T:submit %4.4f" % (t2 - t1)) 117 ToStdout("T:exec %4.4f" % (t3 - t2)) 118 ToStdout("T:total %4.4f" % (t3 - t1)) 119 return 0
120 121
122 -def TestAllocator(opts, args):
123 """Runs the test allocator opcode. 124 125 @param opts: the command line options selected by the user 126 @type args: list 127 @param args: should contain only one element, the iallocator name 128 @rtype: int 129 @return: the desired exit code 130 131 """ 132 try: 133 disks = [{ 134 constants.IDISK_SIZE: utils.ParseUnit(val), 135 constants.IDISK_MODE: constants.DISK_RDWR, 136 } for val in opts.disks.split(",")] 137 except errors.UnitParseError, err: 138 ToStderr("Invalid disks parameter '%s': %s", opts.disks, err) 139 return 1 140 141 nics = [val.split("/") for val in opts.nics.split(",")] 142 for row in nics: 143 while len(row) < 3: 144 row.append(None) 145 for i in range(3): 146 if row[i] == "": 147 row[i] = None 148 nic_dict = [{ 149 constants.INIC_MAC: v[0], 150 constants.INIC_IP: v[1], 151 # The iallocator interface defines a "bridge" item 152 "bridge": v[2], 153 } for v in nics] 154 155 if opts.tags is None: 156 opts.tags = [] 157 else: 158 opts.tags = opts.tags.split(",") 159 if opts.target_groups is None: 160 target_groups = [] 161 else: 162 target_groups = opts.target_groups 163 164 op = opcodes.OpTestAllocator(mode=opts.mode, 165 name=args[0], 166 instances=args, 167 memory=opts.memory, 168 disks=disks, 169 disk_template=opts.disk_template, 170 nics=nic_dict, 171 os=opts.os, 172 vcpus=opts.vcpus, 173 tags=opts.tags, 174 direction=opts.direction, 175 allocator=opts.iallocator, 176 evac_mode=opts.evac_mode, 177 target_groups=target_groups) 178 result = SubmitOpCode(op, opts=opts) 179 ToStdout("%s" % result) 180 return 0
181 182
183 -def _TestJobDependency(opts):
184 """Tests job dependencies. 185 186 """ 187 ToStdout("Testing job dependencies") 188 189 cl = cli.GetClient() 190 191 try: 192 SubmitOpCode(opcodes.OpTestDelay(duration=0, depends=[(-1, None)]), cl=cl) 193 except errors.GenericError, err: 194 if opts.debug: 195 ToStdout("Ignoring error: %s", err) 196 else: 197 raise errors.OpExecError("Submitting plain opcode with relative job ID" 198 " did not fail as expected") 199 200 # TODO: Test dependencies on errors 201 jobs = [ 202 [opcodes.OpTestDelay(duration=1)], 203 [opcodes.OpTestDelay(duration=1, 204 depends=[(-1, [])])], 205 [opcodes.OpTestDelay(duration=1, 206 depends=[(-2, [constants.JOB_STATUS_SUCCESS])])], 207 [opcodes.OpTestDelay(duration=1, 208 depends=[])], 209 [opcodes.OpTestDelay(duration=1, 210 depends=[(-2, [constants.JOB_STATUS_SUCCESS])])], 211 ] 212 213 # Function for checking result 214 check_fn = ht.TListOf(ht.TAnd(ht.TIsLength(2), 215 ht.TItems([ht.TBool, 216 ht.TOr(ht.TNonEmptyString, 217 ht.TJobId)]))) 218 219 result = cl.SubmitManyJobs(jobs) 220 if not check_fn(result): 221 raise errors.OpExecError("Job submission doesn't match %s: %s" % 222 (check_fn, result)) 223 224 # Wait for jobs to finish 225 jex = JobExecutor(cl=cl, opts=opts) 226 227 for (status, job_id) in result: 228 jex.AddJobId(None, status, job_id) 229 230 job_results = jex.GetResults() 231 if not compat.all(row[0] for row in job_results): 232 raise errors.OpExecError("At least one of the submitted jobs failed: %s" % 233 job_results) 234 235 # Get details about jobs 236 data = cl.QueryJobs([job_id for (_, job_id) in result], 237 ["id", "opexec", "ops"]) 238 data_job_id = [job_id for (job_id, _, _) in data] 239 data_opexec = [opexec for (_, opexec, _) in data] 240 data_op = [[opcodes.OpCode.LoadOpCode(op) for op in ops] 241 for (_, _, ops) in data] 242 243 assert compat.all(not op.depends or len(op.depends) == 1 244 for ops in data_op 245 for op in ops) 246 247 # Check resolved job IDs in dependencies 248 for (job_idx, res_jobdep) in [(1, data_job_id[0]), 249 (2, data_job_id[0]), 250 (4, data_job_id[2])]: 251 if data_op[job_idx][0].depends[0][0] != res_jobdep: 252 raise errors.OpExecError("Job %s's opcode doesn't depend on correct job" 253 " ID (%s)" % (job_idx, res_jobdep)) 254 255 # Check execution order 256 if not (data_opexec[0] <= data_opexec[1] and 257 data_opexec[0] <= data_opexec[2] and 258 data_opexec[2] <= data_opexec[4]): 259 raise errors.OpExecError("Jobs did not run in correct order: %s" % data) 260 261 assert len(jobs) == 5 and compat.all(len(ops) == 1 for ops in jobs) 262 263 ToStdout("Job dependency tests were successful")
264 265
266 -def _TestJobSubmission(opts):
267 """Tests submitting jobs. 268 269 """ 270 ToStdout("Testing job submission") 271 272 testdata = [ 273 (0, 0, constants.OP_PRIO_LOWEST), 274 (0, 0, constants.OP_PRIO_HIGHEST), 275 ] 276 277 for priority in (constants.OP_PRIO_SUBMIT_VALID | 278 frozenset([constants.OP_PRIO_LOWEST, 279 constants.OP_PRIO_HIGHEST])): 280 for offset in [-1, +1]: 281 testdata.extend([ 282 (0, 0, priority + offset), 283 (3, 0, priority + offset), 284 (0, 3, priority + offset), 285 (4, 2, priority + offset), 286 ]) 287 288 cl = cli.GetClient() 289 290 for before, after, failpriority in testdata: 291 ops = [] 292 ops.extend([opcodes.OpTestDelay(duration=0) for _ in range(before)]) 293 ops.append(opcodes.OpTestDelay(duration=0, priority=failpriority)) 294 ops.extend([opcodes.OpTestDelay(duration=0) for _ in range(after)]) 295 296 try: 297 cl.SubmitJob(ops) 298 except errors.GenericError, err: 299 if opts.debug: 300 ToStdout("Ignoring error: %s", err) 301 else: 302 raise errors.OpExecError("Submitting opcode with priority %s did not" 303 " fail when it should (allowed are %s)" % 304 (failpriority, constants.OP_PRIO_SUBMIT_VALID)) 305 306 jobs = [ 307 [opcodes.OpTestDelay(duration=0), 308 opcodes.OpTestDelay(duration=0, dry_run=False), 309 opcodes.OpTestDelay(duration=0, dry_run=True)], 310 ops, 311 ] 312 result = cl.SubmitManyJobs(jobs) 313 if not (len(result) == 2 and 314 compat.all(len(i) == 2 for i in result) and 315 compat.all(isinstance(i[1], basestring) for i in result) and 316 result[0][0] and not result[1][0]): 317 raise errors.OpExecError("Submitting multiple jobs did not work as" 318 " expected, result %s" % result) 319 assert len(result) == 2 320 321 ToStdout("Job submission tests were successful")
322 323
324 -class _JobQueueTestReporter(cli.StdioJobPollReportCb):
325 - def __init__(self):
326 """Initializes this class. 327 328 """ 329 cli.StdioJobPollReportCb.__init__(self) 330 self._expected_msgcount = 0 331 self._all_testmsgs = [] 332 self._testmsgs = None 333 self._job_id = None
334
335 - def GetTestMessages(self):
336 """Returns all test log messages received so far. 337 338 """ 339 return self._all_testmsgs
340
341 - def GetJobId(self):
342 """Returns the job ID. 343 344 """ 345 return self._job_id
346
347 - def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
348 """Handles a log message. 349 350 """ 351 if self._job_id is None: 352 self._job_id = job_id 353 elif self._job_id != job_id: 354 raise errors.ProgrammerError("The same reporter instance was used for" 355 " more than one job") 356 357 if log_type == constants.ELOG_JQUEUE_TEST: 358 (sockname, test, arg) = log_msg 359 return self._ProcessTestMessage(job_id, sockname, test, arg) 360 361 elif (log_type == constants.ELOG_MESSAGE and 362 log_msg.startswith(constants.JQT_MSGPREFIX)): 363 if self._testmsgs is None: 364 raise errors.OpExecError("Received test message without a preceding" 365 " start message") 366 testmsg = log_msg[len(constants.JQT_MSGPREFIX):] 367 self._testmsgs.append(testmsg) 368 self._all_testmsgs.append(testmsg) 369 return 370 371 return cli.StdioJobPollReportCb.ReportLogMessage(self, job_id, serial, 372 timestamp, log_type, 373 log_msg)
374
375 - def _ProcessTestMessage(self, job_id, sockname, test, arg):
376 """Handles a job queue test message. 377 378 """ 379 if test not in constants.JQT_ALL: 380 raise errors.OpExecError("Received invalid test message %s" % test) 381 382 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 383 try: 384 sock.settimeout(30.0) 385 386 logging.debug("Connecting to %s", sockname) 387 sock.connect(sockname) 388 389 logging.debug("Checking status") 390 jobdetails = cli.GetClient().QueryJobs([job_id], ["status"])[0] 391 if not jobdetails: 392 raise errors.OpExecError("Can't find job %s" % job_id) 393 394 status = jobdetails[0] 395 396 logging.debug("Status of job %s is %s", job_id, status) 397 398 if test == constants.JQT_EXPANDNAMES: 399 if status != constants.JOB_STATUS_WAITING: 400 raise errors.OpExecError("Job status while expanding names is '%s'," 401 " not '%s' as expected" % 402 (status, constants.JOB_STATUS_WAITING)) 403 elif test in (constants.JQT_EXEC, constants.JQT_LOGMSG): 404 if status != constants.JOB_STATUS_RUNNING: 405 raise errors.OpExecError("Job status while executing opcode is '%s'," 406 " not '%s' as expected" % 407 (status, constants.JOB_STATUS_RUNNING)) 408 409 if test == constants.JQT_STARTMSG: 410 logging.debug("Expecting %s test messages", arg) 411 self._testmsgs = [] 412 elif test == constants.JQT_LOGMSG: 413 if len(self._testmsgs) != arg: 414 raise errors.OpExecError("Received %s test messages when %s are" 415 " expected" % (len(self._testmsgs), arg)) 416 finally: 417 logging.debug("Closing socket") 418 sock.close()
419 420
421 -def TestJobqueue(opts, _):
422 """Runs a few tests on the job queue. 423 424 """ 425 _TestJobSubmission(opts) 426 _TestJobDependency(opts) 427 428 (TM_SUCCESS, 429 TM_MULTISUCCESS, 430 TM_FAIL, 431 TM_PARTFAIL) = range(4) 432 TM_ALL = frozenset([TM_SUCCESS, TM_MULTISUCCESS, TM_FAIL, TM_PARTFAIL]) 433 434 for mode in TM_ALL: 435 test_messages = [ 436 "Testing mode %s" % mode, 437 "Hello World", 438 "A", 439 "", 440 "B" 441 "Foo|bar|baz", 442 utils.TimestampForFilename(), 443 ] 444 445 fail = mode in (TM_FAIL, TM_PARTFAIL) 446 447 if mode == TM_PARTFAIL: 448 ToStdout("Testing partial job failure") 449 ops = [ 450 opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True, 451 log_messages=test_messages, fail=False), 452 opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True, 453 log_messages=test_messages, fail=False), 454 opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True, 455 log_messages=test_messages, fail=True), 456 opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True, 457 log_messages=test_messages, fail=False), 458 ] 459 expect_messages = 3 * [test_messages] 460 expect_opstatus = [ 461 constants.OP_STATUS_SUCCESS, 462 constants.OP_STATUS_SUCCESS, 463 constants.OP_STATUS_ERROR, 464 constants.OP_STATUS_ERROR, 465 ] 466 expect_resultlen = 2 467 elif mode == TM_MULTISUCCESS: 468 ToStdout("Testing multiple successful opcodes") 469 ops = [ 470 opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True, 471 log_messages=test_messages, fail=False), 472 opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True, 473 log_messages=test_messages, fail=False), 474 ] 475 expect_messages = 2 * [test_messages] 476 expect_opstatus = [ 477 constants.OP_STATUS_SUCCESS, 478 constants.OP_STATUS_SUCCESS, 479 ] 480 expect_resultlen = 2 481 else: 482 if mode == TM_SUCCESS: 483 ToStdout("Testing job success") 484 expect_opstatus = [constants.OP_STATUS_SUCCESS] 485 elif mode == TM_FAIL: 486 ToStdout("Testing job failure") 487 expect_opstatus = [constants.OP_STATUS_ERROR] 488 else: 489 raise errors.ProgrammerError("Unknown test mode %s" % mode) 490 491 ops = [ 492 opcodes.OpTestJqueue(notify_waitlock=True, 493 notify_exec=True, 494 log_messages=test_messages, 495 fail=fail) 496 ] 497 expect_messages = [test_messages] 498 expect_resultlen = 1 499 500 cl = cli.GetClient() 501 cli.SetGenericOpcodeOpts(ops, opts) 502 503 # Send job to master daemon 504 job_id = cli.SendJob(ops, cl=cl) 505 506 reporter = _JobQueueTestReporter() 507 results = None 508 509 try: 510 results = cli.PollJob(job_id, cl=cl, reporter=reporter) 511 except errors.OpExecError, err: 512 if not fail: 513 raise 514 ToStdout("Ignoring error: %s", err) 515 else: 516 if fail: 517 raise errors.OpExecError("Job didn't fail when it should") 518 519 # Check length of result 520 if fail: 521 if results is not None: 522 raise errors.OpExecError("Received result from failed job") 523 elif len(results) != expect_resultlen: 524 raise errors.OpExecError("Received %s results (%s), expected %s" % 525 (len(results), results, expect_resultlen)) 526 527 # Check received log messages 528 all_messages = [i for j in expect_messages for i in j] 529 if reporter.GetTestMessages() != all_messages: 530 raise errors.OpExecError("Received test messages don't match input" 531 " (input %r, received %r)" % 532 (all_messages, reporter.GetTestMessages())) 533 534 # Check final status 535 reported_job_id = reporter.GetJobId() 536 if reported_job_id != job_id: 537 raise errors.OpExecError("Reported job ID %s doesn't match" 538 "submission job ID %s" % 539 (reported_job_id, job_id)) 540 541 jobdetails = cli.GetClient().QueryJobs([job_id], ["status", "opstatus"])[0] 542 if not jobdetails: 543 raise errors.OpExecError("Can't find job %s" % job_id) 544 545 if fail: 546 exp_status = constants.JOB_STATUS_ERROR 547 else: 548 exp_status = constants.JOB_STATUS_SUCCESS 549 550 (final_status, final_opstatus) = jobdetails 551 if final_status != exp_status: 552 raise errors.OpExecError("Final job status is %s, not %s as expected" % 553 (final_status, exp_status)) 554 if len(final_opstatus) != len(ops): 555 raise errors.OpExecError("Did not receive status for all opcodes (got %s," 556 " expected %s)" % 557 (len(final_opstatus), len(ops))) 558 if final_opstatus != expect_opstatus: 559 raise errors.OpExecError("Opcode status is %s, expected %s" % 560 (final_opstatus, expect_opstatus)) 561 562 ToStdout("Job queue test successful") 563 564 return 0
565 566
567 -def ListLocks(opts, args): # pylint: disable=W0613
568 """List all locks. 569 570 @param opts: the command line options selected by the user 571 @type args: list 572 @param args: should be an empty list 573 @rtype: int 574 @return: the desired exit code 575 576 """ 577 selected_fields = ParseFields(opts.output, _LIST_LOCKS_DEF_FIELDS) 578 579 def _DashIfNone(fn): 580 def wrapper(value): 581 if not value: 582 return "-" 583 return fn(value) 584 return wrapper 585 586 def _FormatPending(value): 587 """Format pending acquires. 588 589 """ 590 return utils.CommaJoin("%s:%s" % (mode, ",".join(threads)) 591 for mode, threads in value) 592 593 # Format raw values 594 fmtoverride = { 595 "mode": (_DashIfNone(str), False), 596 "owner": (_DashIfNone(",".join), False), 597 "pending": (_DashIfNone(_FormatPending), False), 598 } 599 600 while True: 601 ret = GenericList(constants.QR_LOCK, selected_fields, None, None, 602 opts.separator, not opts.no_headers, 603 format_override=fmtoverride, verbose=opts.verbose) 604 605 if ret != constants.EXIT_SUCCESS: 606 return ret 607 608 if not opts.interval: 609 break 610 611 ToStdout("") 612 time.sleep(opts.interval) 613 614 return 0 615 616 617 commands = { 618 "delay": ( 619 Delay, [ArgUnknown(min=1, max=1)], 620 [cli_option("--no-master", dest="on_master", default=True, 621 action="store_false", help="Do not sleep in the master code"), 622 cli_option("-n", dest="on_nodes", default=[], 623 action="append", help="Select nodes to sleep on"), 624 cli_option("-r", "--repeat", type="int", default="0", dest="repeat", 625 help="Number of times to repeat the sleep"), 626 DRY_RUN_OPT, PRIORITY_OPT, 627 ], 628 "[opts...] <duration>", "Executes a TestDelay OpCode"), 629 "submit-job": ( 630 GenericOpCodes, [ArgFile(min=1)], 631 [VERBOSE_OPT, 632 cli_option("--op-repeat", type="int", default="1", dest="rep_op", 633 help="Repeat the opcode sequence this number of times"), 634 cli_option("--job-repeat", type="int", default="1", dest="rep_job", 635 help="Repeat the job this number of times"), 636 cli_option("--timing-stats", default=False, 637 action="store_true", help="Show timing stats"), 638 cli_option("--each", default=False, action="store_true", 639 help="Submit each job separately"), 640 DRY_RUN_OPT, PRIORITY_OPT, 641 ], 642 "<op_list_file...>", "Submits jobs built from json files" 643 " containing a list of serialized opcodes"), 644 "iallocator": ( 645 TestAllocator, [ArgUnknown(min=1)], 646 [cli_option("--dir", dest="direction", default=constants.IALLOCATOR_DIR_IN, 647 choices=list(constants.VALID_IALLOCATOR_DIRECTIONS), 648 help="Show allocator input (in) or allocator" 649 " results (out)"), 650 IALLOCATOR_OPT, 651 cli_option("-m", "--mode", default="relocate", 652 choices=list(constants.VALID_IALLOCATOR_MODES), 653 help=("Request mode (one of %s)" % 654 utils.CommaJoin(constants.VALID_IALLOCATOR_MODES))), 655 cli_option("--memory", default=128, type="unit", 656 help="Memory size for the instance (MiB)"), 657 cli_option("--disks", default="4096,4096", 658 help="Comma separated list of disk sizes (MiB)"), 659 DISK_TEMPLATE_OPT, 660 cli_option("--nics", default="00:11:22:33:44:55", 661 help="Comma separated list of nics, each nic" 662 " definition is of form mac/ip/bridge, if" 663 " missing values are replace by None"), 664 OS_OPT, 665 cli_option("-p", "--vcpus", default=1, type="int", 666 help="Select number of VCPUs for the instance"), 667 cli_option("--tags", default=None, 668 help="Comma separated list of tags"), 669 cli_option("--evac-mode", default=constants.IALLOCATOR_NEVAC_ALL, 670 choices=list(constants.IALLOCATOR_NEVAC_MODES), 671 help=("Node evacuation mode (one of %s)" % 672 utils.CommaJoin(constants.IALLOCATOR_NEVAC_MODES))), 673 cli_option("--target-groups", help="Target groups for relocation", 674 default=[], action="append"), 675 DRY_RUN_OPT, PRIORITY_OPT, 676 ], 677 "{opts...} <instance>", "Executes a TestAllocator OpCode"), 678 "test-jobqueue": ( 679 TestJobqueue, ARGS_NONE, [PRIORITY_OPT], 680 "", "Test a few aspects of the job queue"), 681 "locks": ( 682 ListLocks, ARGS_NONE, 683 [NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT, VERBOSE_OPT], 684 "[--interval N]", "Show a list of locks in the master daemon"), 685 } 686 687 #: dictionary with aliases for commands 688 aliases = { 689 "allocator": "iallocator", 690 } 691 692
693 -def Main():
694 return GenericMain(commands, aliases=aliases)
695