Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_debug.py @ f33307bd

History | View | Annotate | Download (23.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2010, 2011, 2012 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
from ganeti import wconfd
42

    
43

    
44
#: Default fields for L{ListLocks}
45
_LIST_LOCKS_DEF_FIELDS = [
46
  "name",
47
  "mode",
48
  "owner",
49
  "pending",
50
  ]
51

    
52

    
53
def Delay(opts, args):
54
  """Sleeps for a while
55

56
  @param opts: the command line options selected by the user
57
  @type args: list
58
  @param args: should contain only one element, the duration
59
      the sleep
60
  @rtype: int
61
  @return: the desired exit code
62

63
  """
64
  delay = float(args[0])
65
  op = opcodes.OpTestDelay(duration=delay,
66
                           on_master=opts.on_master,
67
                           on_nodes=opts.on_nodes,
68
                           repeat=opts.repeat)
69
  SubmitOrSend(op, opts)
70

    
71
  return 0
72

    
73

    
74
def GenericOpCodes(opts, args):
75
  """Send any opcode to the master.
76

77
  @param opts: the command line options selected by the user
78
  @type args: list
79
  @param args: should contain only one element, the path of
80
      the file with the opcode definition
81
  @rtype: int
82
  @return: the desired exit code
83

84
  """
85
  cl = cli.GetClient()
86
  jex = cli.JobExecutor(cl=cl, verbose=opts.verbose, opts=opts)
87

    
88
  job_cnt = 0
89
  op_cnt = 0
90
  if opts.timing_stats:
91
    ToStdout("Loading...")
92
  for job_idx in range(opts.rep_job):
93
    for fname in args:
94
      # pylint: disable=W0142
95
      op_data = simplejson.loads(utils.ReadFile(fname))
96
      op_list = [opcodes.OpCode.LoadOpCode(val) for val in op_data]
97
      op_list = op_list * opts.rep_op
98
      jex.QueueJob("file %s/%d" % (fname, job_idx), *op_list)
99
      op_cnt += len(op_list)
100
      job_cnt += 1
101

    
102
  if opts.timing_stats:
103
    t1 = time.time()
104
    ToStdout("Submitting...")
105

    
106
  jex.SubmitPending(each=opts.each)
107

    
108
  if opts.timing_stats:
109
    t2 = time.time()
110
    ToStdout("Executing...")
111

    
112
  jex.GetResults()
113
  if opts.timing_stats:
114
    t3 = time.time()
115
    ToStdout("C:op     %4d" % op_cnt)
116
    ToStdout("C:job    %4d" % job_cnt)
117
    ToStdout("T:submit %4.4f" % (t2 - t1))
118
    ToStdout("T:exec   %4.4f" % (t3 - t2))
119
    ToStdout("T:total  %4.4f" % (t3 - t1))
120
  return 0
121

    
122

    
123
def TestAllocator(opts, args):
124
  """Runs the test allocator opcode.
125

126
  @param opts: the command line options selected by the user
127
  @type args: list
128
  @param args: should contain only one element, the iallocator name
129
  @rtype: int
130
  @return: the desired exit code
131

132
  """
133
  try:
134
    disks = [{
135
      constants.IDISK_SIZE: utils.ParseUnit(val),
136
      constants.IDISK_MODE: constants.DISK_RDWR,
137
      } for val in opts.disks.split(",")]
138
  except errors.UnitParseError, err:
139
    ToStderr("Invalid disks parameter '%s': %s", opts.disks, err)
140
    return 1
141

    
142
  nics = [val.split("/") for val in opts.nics.split(",")]
143
  for row in nics:
144
    while len(row) < 3:
145
      row.append(None)
146
    for i in range(3):
147
      if row[i] == "":
148
        row[i] = None
149
  nic_dict = [{
150
    constants.INIC_MAC: v[0],
151
    constants.INIC_IP: v[1],
152
    # The iallocator interface defines a "bridge" item
153
    "bridge": v[2],
154
    } for v in nics]
155

    
156
  if opts.tags is None:
157
    opts.tags = []
158
  else:
159
    opts.tags = opts.tags.split(",")
160
  if opts.target_groups is None:
161
    target_groups = []
162
  else:
163
    target_groups = opts.target_groups
164

    
165
  op = opcodes.OpTestAllocator(mode=opts.mode,
166
                               name=args[0],
167
                               instances=args,
168
                               memory=opts.memory,
169
                               disks=disks,
170
                               disk_template=opts.disk_template,
171
                               nics=nic_dict,
172
                               os=opts.os,
173
                               vcpus=opts.vcpus,
174
                               tags=opts.tags,
175
                               direction=opts.direction,
176
                               iallocator=opts.iallocator,
177
                               evac_mode=opts.evac_mode,
178
                               target_groups=target_groups,
179
                               spindle_use=opts.spindle_use,
180
                               count=opts.count)
181
  result = SubmitOpCode(op, opts=opts)
182
  ToStdout("%s" % result)
183
  return 0
184

    
185

    
186
def _TestJobDependency(opts):
187
  """Tests job dependencies.
188

189
  """
190
  ToStdout("Testing job dependencies")
191

    
192
  try:
193
    cl = cli.GetClient()
194
    SubmitOpCode(opcodes.OpTestDelay(duration=0, depends=[(-1, None)]), cl=cl)
195
  except errors.GenericError, err:
196
    if opts.debug:
197
      ToStdout("Ignoring error for 'wrong dependencies' test: %s", err)
198
  else:
199
    raise errors.OpExecError("Submitting plain opcode with relative job ID"
200
                             " did not fail as expected")
201

    
202
  # TODO: Test dependencies on errors
203
  jobs = [
204
    [opcodes.OpTestDelay(duration=1)],
205
    [opcodes.OpTestDelay(duration=1,
206
                         depends=[(-1, [])])],
207
    [opcodes.OpTestDelay(duration=1,
208
                         depends=[(-2, [constants.JOB_STATUS_SUCCESS])])],
209
    [opcodes.OpTestDelay(duration=1,
210
                         depends=[])],
211
    [opcodes.OpTestDelay(duration=1,
212
                         depends=[(-2, [constants.JOB_STATUS_SUCCESS])])],
213
    ]
214

    
215
  # Function for checking result
216
  check_fn = ht.TListOf(ht.TAnd(ht.TIsLength(2),
217
                                ht.TItems([ht.TBool,
218
                                           ht.TOr(ht.TNonEmptyString,
219
                                                  ht.TJobId)])))
220

    
221
  cl = cli.GetClient()
222
  result = cl.SubmitManyJobs(jobs)
223
  if not check_fn(result):
224
    raise errors.OpExecError("Job submission doesn't match %s: %s" %
225
                             (check_fn, result))
226

    
227
  # Wait for jobs to finish
228
  jex = JobExecutor(cl=cl, opts=opts)
229

    
230
  for (status, job_id) in result:
231
    jex.AddJobId(None, status, job_id)
232

    
233
  job_results = jex.GetResults()
234
  if not compat.all(row[0] for row in job_results):
235
    raise errors.OpExecError("At least one of the submitted jobs failed: %s" %
236
                             job_results)
237

    
238
  # Get details about jobs
239
  data = cl.QueryJobs([job_id for (_, job_id) in result],
240
                      ["id", "opexec", "ops"])
241
  data_job_id = [job_id for (job_id, _, _) in data]
242
  data_opexec = [opexec for (_, opexec, _) in data]
243
  data_op = [[opcodes.OpCode.LoadOpCode(op) for op in ops]
244
             for (_, _, ops) in data]
245

    
246
  assert compat.all(not op.depends or len(op.depends) == 1
247
                    for ops in data_op
248
                    for op in ops)
249

    
250
  # Check resolved job IDs in dependencies
251
  for (job_idx, res_jobdep) in [(1, data_job_id[0]),
252
                                (2, data_job_id[0]),
253
                                (4, data_job_id[2])]:
254
    if data_op[job_idx][0].depends[0][0] != res_jobdep:
255
      raise errors.OpExecError("Job %s's opcode doesn't depend on correct job"
256
                               " ID (%s)" % (job_idx, res_jobdep))
257

    
258
  # Check execution order
259
  if not (data_opexec[0] <= data_opexec[1] and
260
          data_opexec[0] <= data_opexec[2] and
261
          data_opexec[2] <= data_opexec[4]):
262
    raise errors.OpExecError("Jobs did not run in correct order: %s" % data)
263

    
264
  assert len(jobs) == 5 and compat.all(len(ops) == 1 for ops in jobs)
265

    
266
  ToStdout("Job dependency tests were successful")
267

    
268

    
269
def _TestJobSubmission(opts):
270
  """Tests submitting jobs.
271

272
  """
273
  ToStdout("Testing job submission")
274

    
275
  testdata = [
276
    (0, 0, constants.OP_PRIO_LOWEST),
277
    (0, 0, constants.OP_PRIO_HIGHEST),
278
    ]
279

    
280
  for priority in (constants.OP_PRIO_SUBMIT_VALID |
281
                   frozenset([constants.OP_PRIO_LOWEST,
282
                              constants.OP_PRIO_HIGHEST])):
283
    for offset in [-1, +1]:
284
      testdata.extend([
285
        (0, 0, priority + offset),
286
        (3, 0, priority + offset),
287
        (0, 3, priority + offset),
288
        (4, 2, priority + offset),
289
        ])
290

    
291
  for before, after, failpriority in testdata:
292
    ops = []
293
    ops.extend([opcodes.OpTestDelay(duration=0) for _ in range(before)])
294
    ops.append(opcodes.OpTestDelay(duration=0, priority=failpriority))
295
    ops.extend([opcodes.OpTestDelay(duration=0) for _ in range(after)])
296

    
297
    try:
298
      cl = cli.GetClient()
299
      cl.SubmitJob(ops)
300
    except errors.GenericError, err:
301
      if opts.debug:
302
        ToStdout("Ignoring error for 'wrong priority' test: %s", err)
303
    else:
304
      raise errors.OpExecError("Submitting opcode with priority %s did not"
305
                               " fail when it should (allowed are %s)" %
306
                               (failpriority, constants.OP_PRIO_SUBMIT_VALID))
307

    
308
    jobs = [
309
      [opcodes.OpTestDelay(duration=0),
310
       opcodes.OpTestDelay(duration=0, dry_run=False),
311
       opcodes.OpTestDelay(duration=0, dry_run=True)],
312
      ops,
313
      ]
314
    try:
315
      cl = cli.GetClient()
316
      cl.SubmitManyJobs(jobs)
317
    except errors.GenericError, err:
318
      if opts.debug:
319
        ToStdout("Ignoring error for 'wrong priority' test: %s", err)
320
    else:
321
      raise errors.OpExecError("Submitting manyjobs with an incorrect one"
322
                               " did not fail when it should.")
323
  ToStdout("Job submission tests were successful")
324

    
325

    
326
class _JobQueueTestReporter(cli.StdioJobPollReportCb):
327
  def __init__(self):
328
    """Initializes this class.
329

330
    """
331
    cli.StdioJobPollReportCb.__init__(self)
332
    self._expected_msgcount = 0
333
    self._all_testmsgs = []
334
    self._testmsgs = None
335
    self._job_id = None
336

    
337
  def GetTestMessages(self):
338
    """Returns all test log messages received so far.
339

340
    """
341
    return self._all_testmsgs
342

    
343
  def GetJobId(self):
344
    """Returns the job ID.
345

346
    """
347
    return self._job_id
348

    
349
  def ReportLogMessage(self, job_id, serial, timestamp, log_type, log_msg):
350
    """Handles a log message.
351

352
    """
353
    if self._job_id is None:
354
      self._job_id = job_id
355
    elif self._job_id != job_id:
356
      raise errors.ProgrammerError("The same reporter instance was used for"
357
                                   " more than one job")
358

    
359
    if log_type == constants.ELOG_JQUEUE_TEST:
360
      (sockname, test, arg) = log_msg
361
      return self._ProcessTestMessage(job_id, sockname, test, arg)
362

    
363
    elif (log_type == constants.ELOG_MESSAGE and
364
          log_msg.startswith(constants.JQT_MSGPREFIX)):
365
      if self._testmsgs is None:
366
        raise errors.OpExecError("Received test message without a preceding"
367
                                 " start message")
368
      testmsg = log_msg[len(constants.JQT_MSGPREFIX):]
369
      self._testmsgs.append(testmsg)
370
      self._all_testmsgs.append(testmsg)
371
      return
372

    
373
    return cli.StdioJobPollReportCb.ReportLogMessage(self, job_id, serial,
374
                                                     timestamp, log_type,
375
                                                     log_msg)
376

    
377
  def _ProcessTestMessage(self, job_id, sockname, test, arg):
378
    """Handles a job queue test message.
379

380
    """
381
    if test not in constants.JQT_ALL:
382
      raise errors.OpExecError("Received invalid test message %s" % test)
383

    
384
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
385
    try:
386
      sock.settimeout(30.0)
387

    
388
      logging.debug("Connecting to %s", sockname)
389
      sock.connect(sockname)
390

    
391
      logging.debug("Checking status")
392
      jobdetails = cli.GetClient().QueryJobs([job_id], ["status"])[0]
393
      if not jobdetails:
394
        raise errors.OpExecError("Can't find job %s" % job_id)
395

    
396
      status = jobdetails[0]
397

    
398
      logging.debug("Status of job %s is %s", job_id, status)
399

    
400
      if test == constants.JQT_EXPANDNAMES:
401
        if status != constants.JOB_STATUS_WAITING:
402
          raise errors.OpExecError("Job status while expanding names is '%s',"
403
                                   " not '%s' as expected" %
404
                                   (status, constants.JOB_STATUS_WAITING))
405
      elif test in (constants.JQT_EXEC, constants.JQT_LOGMSG):
406
        if status != constants.JOB_STATUS_RUNNING:
407
          raise errors.OpExecError("Job status while executing opcode is '%s',"
408
                                   " not '%s' as expected" %
409
                                   (status, constants.JOB_STATUS_RUNNING))
410

    
411
      if test == constants.JQT_STARTMSG:
412
        logging.debug("Expecting %s test messages", arg)
413
        self._testmsgs = []
414
      elif test == constants.JQT_LOGMSG:
415
        if len(self._testmsgs) != arg:
416
          raise errors.OpExecError("Received %s test messages when %s are"
417
                                   " expected" % (len(self._testmsgs), arg))
418
    finally:
419
      logging.debug("Closing socket")
420
      sock.close()
421

    
422

    
423
def TestJobqueue(opts, _):
424
  """Runs a few tests on the job queue.
425

426
  """
427
  _TestJobSubmission(opts)
428
  _TestJobDependency(opts)
429

    
430
  (TM_SUCCESS,
431
   TM_MULTISUCCESS,
432
   TM_FAIL,
433
   TM_PARTFAIL) = range(4)
434
  TM_ALL = compat.UniqueFrozenset([
435
    TM_SUCCESS,
436
    TM_MULTISUCCESS,
437
    TM_FAIL,
438
    TM_PARTFAIL,
439
    ])
440

    
441
  for mode in TM_ALL:
442
    test_messages = [
443
      "Testing mode %s" % mode,
444
      "Hello World",
445
      "A",
446
      "",
447
      "B"
448
      "Foo|bar|baz",
449
      utils.TimestampForFilename(),
450
      ]
451

    
452
    fail = mode in (TM_FAIL, TM_PARTFAIL)
453

    
454
    if mode == TM_PARTFAIL:
455
      ToStdout("Testing partial job failure")
456
      ops = [
457
        opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True,
458
                             log_messages=test_messages, fail=False),
459
        opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True,
460
                             log_messages=test_messages, fail=False),
461
        opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True,
462
                             log_messages=test_messages, fail=True),
463
        opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True,
464
                             log_messages=test_messages, fail=False),
465
        ]
466
      expect_messages = 3 * [test_messages]
467
      expect_opstatus = [
468
        constants.OP_STATUS_SUCCESS,
469
        constants.OP_STATUS_SUCCESS,
470
        constants.OP_STATUS_ERROR,
471
        constants.OP_STATUS_ERROR,
472
        ]
473
      expect_resultlen = 2
474
    elif mode == TM_MULTISUCCESS:
475
      ToStdout("Testing multiple successful opcodes")
476
      ops = [
477
        opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True,
478
                             log_messages=test_messages, fail=False),
479
        opcodes.OpTestJqueue(notify_waitlock=True, notify_exec=True,
480
                             log_messages=test_messages, fail=False),
481
        ]
482
      expect_messages = 2 * [test_messages]
483
      expect_opstatus = [
484
        constants.OP_STATUS_SUCCESS,
485
        constants.OP_STATUS_SUCCESS,
486
        ]
487
      expect_resultlen = 2
488
    else:
489
      if mode == TM_SUCCESS:
490
        ToStdout("Testing job success")
491
        expect_opstatus = [constants.OP_STATUS_SUCCESS]
492
      elif mode == TM_FAIL:
493
        ToStdout("Testing job failure")
494
        expect_opstatus = [constants.OP_STATUS_ERROR]
495
      else:
496
        raise errors.ProgrammerError("Unknown test mode %s" % mode)
497

    
498
      ops = [
499
        opcodes.OpTestJqueue(notify_waitlock=True,
500
                             notify_exec=True,
501
                             log_messages=test_messages,
502
                             fail=fail),
503
        ]
504
      expect_messages = [test_messages]
505
      expect_resultlen = 1
506

    
507
    cl = cli.GetClient()
508
    cli.SetGenericOpcodeOpts(ops, opts)
509

    
510
    # Send job to master daemon
511
    job_id = cli.SendJob(ops, cl=cl)
512

    
513
    reporter = _JobQueueTestReporter()
514
    results = None
515

    
516
    try:
517
      results = cli.PollJob(job_id, cl=cl, reporter=reporter)
518
    except errors.OpExecError, err:
519
      if not fail:
520
        raise
521
      ToStdout("Ignoring error for 'job fail' test: %s", err)
522
    else:
523
      if fail:
524
        raise errors.OpExecError("Job didn't fail when it should")
525

    
526
    # Check length of result
527
    if fail:
528
      if results is not None:
529
        raise errors.OpExecError("Received result from failed job")
530
    elif len(results) != expect_resultlen:
531
      raise errors.OpExecError("Received %s results (%s), expected %s" %
532
                               (len(results), results, expect_resultlen))
533

    
534
    # Check received log messages
535
    all_messages = [i for j in expect_messages for i in j]
536
    if reporter.GetTestMessages() != all_messages:
537
      raise errors.OpExecError("Received test messages don't match input"
538
                               " (input %r, received %r)" %
539
                               (all_messages, reporter.GetTestMessages()))
540

    
541
    # Check final status
542
    reported_job_id = reporter.GetJobId()
543
    if reported_job_id != job_id:
544
      raise errors.OpExecError("Reported job ID %s doesn't match"
545
                               "submission job ID %s" %
546
                               (reported_job_id, job_id))
547

    
548
    jobdetails = cli.GetClient().QueryJobs([job_id], ["status", "opstatus"])[0]
549
    if not jobdetails:
550
      raise errors.OpExecError("Can't find job %s" % job_id)
551

    
552
    if fail:
553
      exp_status = constants.JOB_STATUS_ERROR
554
    else:
555
      exp_status = constants.JOB_STATUS_SUCCESS
556

    
557
    (final_status, final_opstatus) = jobdetails
558
    if final_status != exp_status:
559
      raise errors.OpExecError("Final job status is %s, not %s as expected" %
560
                               (final_status, exp_status))
561
    if len(final_opstatus) != len(ops):
562
      raise errors.OpExecError("Did not receive status for all opcodes (got %s,"
563
                               " expected %s)" %
564
                               (len(final_opstatus), len(ops)))
565
    if final_opstatus != expect_opstatus:
566
      raise errors.OpExecError("Opcode status is %s, expected %s" %
567
                               (final_opstatus, expect_opstatus))
568

    
569
  ToStdout("Job queue test successful")
570

    
571
  return 0
572

    
573

    
574
def ListLocks(opts, args): # pylint: disable=W0613
575
  """List all locks.
576

577
  @param opts: the command line options selected by the user
578
  @type args: list
579
  @param args: should be an empty list
580
  @rtype: int
581
  @return: the desired exit code
582

583
  """
584
  selected_fields = ParseFields(opts.output, _LIST_LOCKS_DEF_FIELDS)
585

    
586
  def _DashIfNone(fn):
587
    def wrapper(value):
588
      if not value:
589
        return "-"
590
      return fn(value)
591
    return wrapper
592

    
593
  def _FormatPending(value):
594
    """Format pending acquires.
595

596
    """
597
    return utils.CommaJoin("%s:%s" % (mode, ",".join(map(str, threads)))
598
                           for mode, threads in value)
599

    
600
  # Format raw values
601
  fmtoverride = {
602
    "mode": (_DashIfNone(str), False),
603
    "owner": (_DashIfNone(",".join), False),
604
    "pending": (_DashIfNone(_FormatPending), False),
605
    }
606

    
607
  while True:
608
    ret = GenericList(constants.QR_LOCK, selected_fields, None, None,
609
                      opts.separator, not opts.no_headers,
610
                      format_override=fmtoverride, verbose=opts.verbose)
611

    
612
    if ret != constants.EXIT_SUCCESS:
613
      return ret
614

    
615
    if not opts.interval:
616
      break
617

    
618
    ToStdout("")
619
    time.sleep(opts.interval)
620

    
621
  return 0
622

    
623

    
624
def Wconfd(opts, args): # pylint: disable=W0613
625
  """Send commands to WConfD.
626

627
  @param opts: the command line options selected by the user
628
  @type args: list
629
  @param args: the command to send, followed by the command-specific arguments
630
  @rtype: int
631
  @return: the desired exit code
632

633
  """
634
  if args[0] == "echo":
635
    if len(args) != 2:
636
      ToStderr("Command 'echo' takes only precisely argument.")
637
      return 1
638
    result = wconfd.Client().Echo(args[1])
639
    print "Answer: %s" % (result,)
640
  else:
641
    ToStderr("Command '%s' not supported", args[0])
642
    return 1
643

    
644
  return 0
645

    
646

    
647
commands = {
648
  "delay": (
649
    Delay, [ArgUnknown(min=1, max=1)],
650
    [cli_option("--no-master", dest="on_master", default=True,
651
                action="store_false", help="Do not sleep in the master code"),
652
     cli_option("-n", dest="on_nodes", default=[],
653
                action="append", help="Select nodes to sleep on"),
654
     cli_option("-r", "--repeat", type="int", default="0", dest="repeat",
655
                help="Number of times to repeat the sleep"),
656
     DRY_RUN_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
657
    "[opts...] <duration>", "Executes a TestDelay OpCode"),
658
  "submit-job": (
659
    GenericOpCodes, [ArgFile(min=1)],
660
    [VERBOSE_OPT,
661
     cli_option("--op-repeat", type="int", default="1", dest="rep_op",
662
                help="Repeat the opcode sequence this number of times"),
663
     cli_option("--job-repeat", type="int", default="1", dest="rep_job",
664
                help="Repeat the job this number of times"),
665
     cli_option("--timing-stats", default=False,
666
                action="store_true", help="Show timing stats"),
667
     cli_option("--each", default=False, action="store_true",
668
                help="Submit each job separately"),
669
     DRY_RUN_OPT, PRIORITY_OPT,
670
     ],
671
    "<op_list_file...>", "Submits jobs built from json files"
672
    " containing a list of serialized opcodes"),
673
  "iallocator": (
674
    TestAllocator, [ArgUnknown(min=1)],
675
    [cli_option("--dir", dest="direction", default=constants.IALLOCATOR_DIR_IN,
676
                choices=list(constants.VALID_IALLOCATOR_DIRECTIONS),
677
                help="Show allocator input (in) or allocator"
678
                " results (out)"),
679
     IALLOCATOR_OPT,
680
     cli_option("-m", "--mode", default="relocate",
681
                choices=list(constants.VALID_IALLOCATOR_MODES),
682
                help=("Request mode (one of %s)" %
683
                      utils.CommaJoin(constants.VALID_IALLOCATOR_MODES))),
684
     cli_option("--memory", default=128, type="unit",
685
                help="Memory size for the instance (MiB)"),
686
     cli_option("--disks", default="4096,4096",
687
                help="Comma separated list of disk sizes (MiB)"),
688
     DISK_TEMPLATE_OPT,
689
     cli_option("--nics", default="00:11:22:33:44:55",
690
                help="Comma separated list of nics, each nic"
691
                " definition is of form mac/ip/bridge, if"
692
                " missing values are replace by None"),
693
     OS_OPT,
694
     cli_option("-p", "--vcpus", default=1, type="int",
695
                help="Select number of VCPUs for the instance"),
696
     cli_option("--tags", default=None,
697
                help="Comma separated list of tags"),
698
     cli_option("--evac-mode", default=constants.NODE_EVAC_ALL,
699
                choices=list(constants.NODE_EVAC_MODES),
700
                help=("Node evacuation mode (one of %s)" %
701
                      utils.CommaJoin(constants.NODE_EVAC_MODES))),
702
     cli_option("--target-groups", help="Target groups for relocation",
703
                default=[], action="append"),
704
     cli_option("--spindle-use", help="How many spindles to use",
705
                default=1, type="int"),
706
     cli_option("--count", help="How many instances to allocate",
707
                default=2, type="int"),
708
     DRY_RUN_OPT, PRIORITY_OPT,
709
     ],
710
    "{opts...} <instance>", "Executes a TestAllocator OpCode"),
711
  "test-jobqueue": (
712
    TestJobqueue, ARGS_NONE, [PRIORITY_OPT],
713
    "", "Test a few aspects of the job queue"),
714
  "locks": (
715
    ListLocks, ARGS_NONE,
716
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT, VERBOSE_OPT],
717
    "[--interval N]", "Show a list of locks in the master daemon"),
718
  "wconfd": (
719
    Wconfd, [ArgUnknown(min=1)], [],
720
    "<cmd> <args...>", "Directly talk to WConfD"),
721
  }
722

    
723
#: dictionary with aliases for commands
724
aliases = {
725
  "allocator": "iallocator",
726
  }
727

    
728

    
729
def Main():
730
  return GenericMain(commands, aliases=aliases)