Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_debug.py @ 653bc0f1

History | View | Annotate | Download (22.9 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

    
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
  SubmitOrSend(op, 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
                               iallocator=opts.iallocator,
176
                               evac_mode=opts.evac_mode,
177
                               target_groups=target_groups,
178
                               spindle_use=opts.spindle_use,
179
                               count=opts.count)
180
  result = SubmitOpCode(op, opts=opts)
181
  ToStdout("%s" % result)
182
  return 0
183

    
184

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

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

    
191
  try:
192
    cl = cli.GetClient()
193
    SubmitOpCode(opcodes.OpTestDelay(duration=0, depends=[(-1, None)]), cl=cl)
194
  except errors.GenericError, err:
195
    if opts.debug:
196
      ToStdout("Ignoring error for 'wrong dependencies' test: %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
  cl = cli.GetClient()
221
  result = cl.SubmitManyJobs(jobs)
222
  if not check_fn(result):
223
    raise errors.OpExecError("Job submission doesn't match %s: %s" %
224
                             (check_fn, result))
225

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

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

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

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

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

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

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

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

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

    
267

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

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

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

    
279
  for priority in (constants.OP_PRIO_SUBMIT_VALID |
280
                   frozenset([constants.OP_PRIO_LOWEST,
281
                              constants.OP_PRIO_HIGHEST])):
282
    for offset in [-1, +1]:
283
      testdata.extend([
284
        (0, 0, priority + offset),
285
        (3, 0, priority + offset),
286
        (0, 3, priority + offset),
287
        (4, 2, priority + offset),
288
        ])
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 = cli.GetClient()
298
      cl.SubmitJob(ops)
299
    except errors.GenericError, err:
300
      if opts.debug:
301
        ToStdout("Ignoring error for 'wrong priority' test: %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
    try:
314
      cl = cli.GetClient()
315
      cl.SubmitManyJobs(jobs)
316
    except errors.GenericError, err:
317
      if opts.debug:
318
        ToStdout("Ignoring error for 'wrong priority' test: %s", err)
319
    else:
320
      raise errors.OpExecError("Submitting manyjobs with an incorrect one"
321
                               " did not fail when it should.")
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 = compat.UniqueFrozenset([
434
    TM_SUCCESS,
435
    TM_MULTISUCCESS,
436
    TM_FAIL,
437
    TM_PARTFAIL,
438
    ])
439

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

    
451
    fail = mode in (TM_FAIL, TM_PARTFAIL)
452

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

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

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

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

    
512
    reporter = _JobQueueTestReporter()
513
    results = None
514

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

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

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

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

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

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

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

    
568
  ToStdout("Job queue test successful")
569

    
570
  return 0
571

    
572

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

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

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

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

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

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

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

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

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

    
614
    if not opts.interval:
615
      break
616

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

    
620
  return 0
621

    
622

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

    
696
#: dictionary with aliases for commands
697
aliases = {
698
  "allocator": "iallocator",
699
  }
700

    
701

    
702
def Main():
703
  return GenericMain(commands, aliases=aliases)