Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_debug.py @ d6f46b6a

History | View | Annotate | Download (22.5 kB)

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-msg=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-msg=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
                               evac_nodes=args,
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
                               allocator=opts.iallocator,
177
                               evac_mode=opts.evac_mode,
178
                               target_groups=target_groups)
179
  result = SubmitOpCode(op, opts=opts)
180
  ToStdout("%s" % result)
181
  return 0
182

    
183

    
184
def _TestJobDependency(opts):
185
  """Tests job dependencies.
186

187
  """
188
  ToStdout("Testing job dependencies")
189

    
190
  cl = cli.GetClient()
191

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

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

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

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

    
225
  # Wait for jobs to finish
226
  jex = JobExecutor(cl=cl, opts=opts)
227

    
228
  for (status, job_id) in result:
229
    jex.AddJobId(None, status, job_id)
230

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

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

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

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

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

    
262
  assert len(jobs) == 5 and compat.all(len(ops) == 1 for ops in jobs)
263

    
264
  ToStdout("Job dependency tests were successful")
265

    
266

    
267
def _TestJobSubmission(opts):
268
  """Tests submitting jobs.
269

270
  """
271
  ToStdout("Testing job submission")
272

    
273
  testdata = [
274
    (0, 0, constants.OP_PRIO_LOWEST),
275
    (0, 0, constants.OP_PRIO_HIGHEST),
276
    ]
277

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

    
289
  cl = cli.GetClient()
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.SubmitJob(ops)
299
    except errors.GenericError, err:
300
      if opts.debug:
301
        ToStdout("Ignoring error: %s", err)
302
    else:
303
      raise errors.OpExecError("Submitting opcode with priority %s did not"
304
                               " fail when it should (allowed are %s)" %
305
                               (failpriority, constants.OP_PRIO_SUBMIT_VALID))
306

    
307
    jobs = [
308
      [opcodes.OpTestDelay(duration=0),
309
       opcodes.OpTestDelay(duration=0, dry_run=False),
310
       opcodes.OpTestDelay(duration=0, dry_run=True)],
311
      ops,
312
      ]
313
    result = cl.SubmitManyJobs(jobs)
314
    if not (len(result) == 2 and
315
            compat.all(len(i) == 2 for i in result) and
316
            compat.all(isinstance(i[1], basestring) for i in result) and
317
            result[0][0] and not result[1][0]):
318
      raise errors.OpExecError("Submitting multiple jobs did not work as"
319
                               " expected, result %s" % result)
320
    assert len(result) == 2
321

    
322
  ToStdout("Job submission tests were successful")
323

    
324

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

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

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

339
    """
340
    return self._all_testmsgs
341

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

345
    """
346
    return self._job_id
347

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

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

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

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

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

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

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

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

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

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

    
395
      status = jobdetails[0]
396

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

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

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

    
421

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

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

    
429
  (TM_SUCCESS,
430
   TM_MULTISUCCESS,
431
   TM_FAIL,
432
   TM_PARTFAIL) = range(4)
433
  TM_ALL = frozenset([TM_SUCCESS, TM_MULTISUCCESS, TM_FAIL, TM_PARTFAIL])
434

    
435
  for mode in TM_ALL:
436
    test_messages = [
437
      "Testing mode %s" % mode,
438
      "Hello World",
439
      "A",
440
      "",
441
      "B"
442
      "Foo|bar|baz",
443
      utils.TimestampForFilename(),
444
      ]
445

    
446
    fail = mode in (TM_FAIL, TM_PARTFAIL)
447

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

    
492
      ops = [
493
        opcodes.OpTestJqueue(notify_waitlock=True,
494
                             notify_exec=True,
495
                             log_messages=test_messages,
496
                             fail=fail)
497
        ]
498
      expect_messages = [test_messages]
499
      expect_resultlen = 1
500

    
501
    cl = cli.GetClient()
502
    cli.SetGenericOpcodeOpts(ops, opts)
503

    
504
    # Send job to master daemon
505
    job_id = cli.SendJob(ops, cl=cl)
506

    
507
    reporter = _JobQueueTestReporter()
508
    results = None
509

    
510
    try:
511
      results = cli.PollJob(job_id, cl=cl, reporter=reporter)
512
    except errors.OpExecError, err:
513
      if not fail:
514
        raise
515
      ToStdout("Ignoring error: %s", err)
516
    else:
517
      if fail:
518
        raise errors.OpExecError("Job didn't fail when it should")
519

    
520
    # Check length of result
521
    if fail:
522
      if results is not None:
523
        raise errors.OpExecError("Received result from failed job")
524
    elif len(results) != expect_resultlen:
525
      raise errors.OpExecError("Received %s results (%s), expected %s" %
526
                               (len(results), results, expect_resultlen))
527

    
528
    # Check received log messages
529
    all_messages = [i for j in expect_messages for i in j]
530
    if reporter.GetTestMessages() != all_messages:
531
      raise errors.OpExecError("Received test messages don't match input"
532
                               " (input %r, received %r)" %
533
                               (all_messages, reporter.GetTestMessages()))
534

    
535
    # Check final status
536
    reported_job_id = reporter.GetJobId()
537
    if reported_job_id != job_id:
538
      raise errors.OpExecError("Reported job ID %s doesn't match"
539
                               "submission job ID %s" %
540
                               (reported_job_id, job_id))
541

    
542
    jobdetails = cli.GetClient().QueryJobs([job_id], ["status", "opstatus"])[0]
543
    if not jobdetails:
544
      raise errors.OpExecError("Can't find job %s" % job_id)
545

    
546
    if fail:
547
      exp_status = constants.JOB_STATUS_ERROR
548
    else:
549
      exp_status = constants.JOB_STATUS_SUCCESS
550

    
551
    (final_status, final_opstatus) = jobdetails
552
    if final_status != exp_status:
553
      raise errors.OpExecError("Final job status is %s, not %s as expected" %
554
                               (final_status, exp_status))
555
    if len(final_opstatus) != len(ops):
556
      raise errors.OpExecError("Did not receive status for all opcodes (got %s,"
557
                               " expected %s)" %
558
                               (len(final_opstatus), len(ops)))
559
    if final_opstatus != expect_opstatus:
560
      raise errors.OpExecError("Opcode status is %s, expected %s" %
561
                               (final_opstatus, expect_opstatus))
562

    
563
  ToStdout("Job queue test successful")
564

    
565
  return 0
566

    
567

    
568
def ListLocks(opts, args): # pylint: disable-msg=W0613
569
  """List all locks.
570

571
  @param opts: the command line options selected by the user
572
  @type args: list
573
  @param args: should be an empty list
574
  @rtype: int
575
  @return: the desired exit code
576

577
  """
578
  selected_fields = ParseFields(opts.output, _LIST_LOCKS_DEF_FIELDS)
579

    
580
  def _DashIfNone(fn):
581
    def wrapper(value):
582
      if not value:
583
        return "-"
584
      return fn(value)
585
    return wrapper
586

    
587
  def _FormatPending(value):
588
    """Format pending acquires.
589

590
    """
591
    return utils.CommaJoin("%s:%s" % (mode, ",".join(threads))
592
                           for mode, threads in value)
593

    
594
  # Format raw values
595
  fmtoverride = {
596
    "mode": (_DashIfNone(str), False),
597
    "owner": (_DashIfNone(",".join), False),
598
    "pending": (_DashIfNone(_FormatPending), False),
599
    }
600

    
601
  while True:
602
    ret = GenericList(constants.QR_LOCK, selected_fields, None, None,
603
                      opts.separator, not opts.no_headers,
604
                      format_override=fmtoverride, verbose=opts.verbose)
605

    
606
    if ret != constants.EXIT_SUCCESS:
607
      return ret
608

    
609
    if not opts.interval:
610
      break
611

    
612
    ToStdout("")
613
    time.sleep(opts.interval)
614

    
615
  return 0
616

    
617

    
618
commands = {
619
  'delay': (
620
    Delay, [ArgUnknown(min=1, max=1)],
621
    [cli_option("--no-master", dest="on_master", default=True,
622
                action="store_false", help="Do not sleep in the master code"),
623
     cli_option("-n", dest="on_nodes", default=[],
624
                action="append", help="Select nodes to sleep on"),
625
     cli_option("-r", "--repeat", type="int", default="0", dest="repeat",
626
                help="Number of times to repeat the sleep"),
627
     DRY_RUN_OPT, PRIORITY_OPT,
628
     ],
629
    "[opts...] <duration>", "Executes a TestDelay OpCode"),
630
  'submit-job': (
631
    GenericOpCodes, [ArgFile(min=1)],
632
    [VERBOSE_OPT,
633
     cli_option("--op-repeat", type="int", default="1", dest="rep_op",
634
                help="Repeat the opcode sequence this number of times"),
635
     cli_option("--job-repeat", type="int", default="1", dest="rep_job",
636
                help="Repeat the job this number of times"),
637
     cli_option("--timing-stats", default=False,
638
                action="store_true", help="Show timing stats"),
639
     cli_option("--each", default=False, action="store_true",
640
                help="Submit each job separately"),
641
     DRY_RUN_OPT, PRIORITY_OPT,
642
     ],
643
    "<op_list_file...>", "Submits jobs built from json files"
644
    " containing a list of serialized opcodes"),
645
  'iallocator': (
646
    TestAllocator, [ArgUnknown(min=1)],
647
    [cli_option("--dir", dest="direction", default=constants.IALLOCATOR_DIR_IN,
648
                choices=list(constants.VALID_IALLOCATOR_DIRECTIONS),
649
                help="Show allocator input (in) or allocator"
650
                " results (out)"),
651
     IALLOCATOR_OPT,
652
     cli_option("-m", "--mode", default="relocate",
653
                choices=list(constants.VALID_IALLOCATOR_MODES),
654
                help=("Request mode (one of %s)" %
655
                      utils.CommaJoin(constants.VALID_IALLOCATOR_MODES))),
656
     cli_option("--memory", default=128, type="unit",
657
                help="Memory size for the instance (MiB)"),
658
     cli_option("--disks", default="4096,4096",
659
                help="Comma separated list of disk sizes (MiB)"),
660
     DISK_TEMPLATE_OPT,
661
     cli_option("--nics", default="00:11:22:33:44:55",
662
                help="Comma separated list of nics, each nic"
663
                " definition is of form mac/ip/bridge, if"
664
                " missing values are replace by None"),
665
     OS_OPT,
666
     cli_option("-p", "--vcpus", default=1, type="int",
667
                help="Select number of VCPUs for the instance"),
668
     cli_option("--tags", default=None,
669
                help="Comma separated list of tags"),
670
     cli_option("--evac-mode", default=constants.IALLOCATOR_NEVAC_ALL,
671
                choices=list(constants.IALLOCATOR_NEVAC_MODES),
672
                help=("Node evacuation mode (one of %s)" %
673
                      utils.CommaJoin(constants.IALLOCATOR_NEVAC_MODES))),
674
     cli_option("--target-groups", help="Target groups for relocation",
675
                default=[], action="append"),
676
     DRY_RUN_OPT, PRIORITY_OPT,
677
     ],
678
    "{opts...} <instance>", "Executes a TestAllocator OpCode"),
679
  "test-jobqueue": (
680
    TestJobqueue, ARGS_NONE, [PRIORITY_OPT],
681
    "", "Test a few aspects of the job queue"),
682
  "locks": (
683
    ListLocks, ARGS_NONE,
684
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT, INTERVAL_OPT, VERBOSE_OPT],
685
    "[--interval N]", "Show a list of locks in the master daemon"),
686
  }
687

    
688
#: dictionary with aliases for commands
689
aliases = {
690
  "allocator": "iallocator",
691
  }
692

    
693
def Main():
694
  return GenericMain(commands, aliases=aliases)