Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_debug.py @ 8a37b426

History | View | Annotate | Download (24.1 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
                           interruptible=opts.interruptible)
70
  SubmitOrSend(op, opts)
71

    
72
  return 0
73

    
74

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

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

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

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

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

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

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

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

    
123

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

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

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

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

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

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

    
186

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
269

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

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

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

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

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

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

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

    
326

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

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

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

341
    """
342
    return self._all_testmsgs
343

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

347
    """
348
    return self._job_id
349

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

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

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

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

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

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

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

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

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

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

    
397
      status = jobdetails[0]
398

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

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

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

    
423

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

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

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

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

    
453
    fail = mode in (TM_FAIL, TM_PARTFAIL)
454

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

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

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

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

    
514
    reporter = _JobQueueTestReporter()
515
    results = None
516

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

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

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

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

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

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

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

    
570
  ToStdout("Job queue test successful")
571

    
572
  return 0
573

    
574

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

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

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

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

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

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

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

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

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

    
616
    if not opts.interval:
617
      break
618

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

    
622
  return 0
623

    
624

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

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

634
  """
635
  if args[0] == "echo":
636
    if len(args) != 2:
637
      ToStderr("Command 'echo' takes only precisely argument.")
638
      return 1
639
    result = wconfd.Client().Echo(args[1])
640
    print "Answer: %s" % (result,)
641
  elif args[0] == "listlocks":
642
    if len(args) != 2:
643
      ToStderr("Command 'listlocks' takes only precisely argument.")
644
      return 1
645
    result = \
646
        wconfd.Client().ListLocks(int(args[1]),
647
                                  utils.livelock.GuessLockfileFor("masterd"))
648
    print "Answer: %s" % (result,)
649
  else:
650
    ToStderr("Command '%s' not supported", args[0])
651
    return 1
652

    
653
  return 0
654

    
655

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

    
736
#: dictionary with aliases for commands
737
aliases = {
738
  "allocator": "iallocator",
739
  }
740

    
741

    
742
def Main():
743
  return GenericMain(commands, aliases=aliases)