Add DCStatus data type for the data collectors
[ganeti-local] / lib / client / gnt_debug.py
index a327321..829ed05 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
+# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
 
 """Debugging commands"""
 
-# pylint: disable-msg=W0401,W0614,C0103
+# pylint: disable=W0401,W0614,C0103
 # W0401: Wildcard import ganeti.cli
 # W0614: Unused import %s from wildcard import (since we need cli)
 # C0103: Invalid name gnt-backup
@@ -37,6 +37,7 @@ from ganeti import opcodes
 from ganeti import utils
 from ganeti import errors
 from ganeti import compat
+from ganeti import ht
 
 
 #: Default fields for L{ListLocks}
@@ -64,7 +65,7 @@ def Delay(opts, args):
                            on_master=opts.on_master,
                            on_nodes=opts.on_nodes,
                            repeat=opts.repeat)
-  SubmitOpCode(op, opts=opts)
+  SubmitOrSend(op, opts)
 
   return 0
 
@@ -89,7 +90,7 @@ def GenericOpCodes(opts, args):
     ToStdout("Loading...")
   for job_idx in range(opts.rep_job):
     for fname in args:
-      # pylint: disable-msg=W0142
+      # pylint: disable=W0142
       op_data = simplejson.loads(utils.ReadFile(fname))
       op_list = [opcodes.OpCode.LoadOpCode(val) for val in op_data]
       op_list = op_list * opts.rep_op
@@ -112,9 +113,9 @@ def GenericOpCodes(opts, args):
     t3 = time.time()
     ToStdout("C:op     %4d" % op_cnt)
     ToStdout("C:job    %4d" % job_cnt)
-    ToStdout("T:submit %4.4f" % (t2-t1))
-    ToStdout("T:exec   %4.4f" % (t3-t2))
-    ToStdout("T:total  %4.4f" % (t3-t1))
+    ToStdout("T:submit %4.4f" % (t2 - t1))
+    ToStdout("T:exec   %4.4f" % (t3 - t2))
+    ToStdout("T:total  %4.4f" % (t3 - t1))
   return 0
 
 
@@ -142,7 +143,7 @@ def TestAllocator(opts, args):
     while len(row) < 3:
       row.append(None)
     for i in range(3):
-      if row[i] == '':
+      if row[i] == "":
         row[i] = None
   nic_dict = [{
     constants.INIC_MAC: v[0],
@@ -155,12 +156,15 @@ def TestAllocator(opts, args):
     opts.tags = []
   else:
     opts.tags = opts.tags.split(",")
+  if opts.target_groups is None:
+    target_groups = []
+  else:
+    target_groups = opts.target_groups
 
   op = opcodes.OpTestAllocator(mode=opts.mode,
                                name=args[0],
-                               evac_nodes=args,
                                instances=args,
-                               mem_size=opts.mem,
+                               memory=opts.memory,
                                disks=disks,
                                disk_template=opts.disk_template,
                                nics=nic_dict,
@@ -168,14 +172,99 @@ def TestAllocator(opts, args):
                                vcpus=opts.vcpus,
                                tags=opts.tags,
                                direction=opts.direction,
-                               allocator=opts.iallocator,
-                               reloc_mode=opts.reloc_mode,
-                               target_groups=opts.target_groups)
+                               iallocator=opts.iallocator,
+                               evac_mode=opts.evac_mode,
+                               target_groups=target_groups,
+                               spindle_use=opts.spindle_use,
+                               count=opts.count)
   result = SubmitOpCode(op, opts=opts)
   ToStdout("%s" % result)
   return 0
 
 
+def _TestJobDependency(opts):
+  """Tests job dependencies.
+
+  """
+  ToStdout("Testing job dependencies")
+
+  cl = cli.GetClient()
+
+  try:
+    SubmitOpCode(opcodes.OpTestDelay(duration=0, depends=[(-1, None)]), cl=cl)
+  except errors.GenericError, err:
+    if opts.debug:
+      ToStdout("Ignoring error for 'wrong dependencies' test: %s", err)
+  else:
+    raise errors.OpExecError("Submitting plain opcode with relative job ID"
+                             " did not fail as expected")
+
+  # TODO: Test dependencies on errors
+  jobs = [
+    [opcodes.OpTestDelay(duration=1)],
+    [opcodes.OpTestDelay(duration=1,
+                         depends=[(-1, [])])],
+    [opcodes.OpTestDelay(duration=1,
+                         depends=[(-2, [constants.JOB_STATUS_SUCCESS])])],
+    [opcodes.OpTestDelay(duration=1,
+                         depends=[])],
+    [opcodes.OpTestDelay(duration=1,
+                         depends=[(-2, [constants.JOB_STATUS_SUCCESS])])],
+    ]
+
+  # Function for checking result
+  check_fn = ht.TListOf(ht.TAnd(ht.TIsLength(2),
+                                ht.TItems([ht.TBool,
+                                           ht.TOr(ht.TNonEmptyString,
+                                                  ht.TJobId)])))
+
+  result = cl.SubmitManyJobs(jobs)
+  if not check_fn(result):
+    raise errors.OpExecError("Job submission doesn't match %s: %s" %
+                             (check_fn, result))
+
+  # Wait for jobs to finish
+  jex = JobExecutor(cl=cl, opts=opts)
+
+  for (status, job_id) in result:
+    jex.AddJobId(None, status, job_id)
+
+  job_results = jex.GetResults()
+  if not compat.all(row[0] for row in job_results):
+    raise errors.OpExecError("At least one of the submitted jobs failed: %s" %
+                             job_results)
+
+  # Get details about jobs
+  data = cl.QueryJobs([job_id for (_, job_id) in result],
+                      ["id", "opexec", "ops"])
+  data_job_id = [job_id for (job_id, _, _) in data]
+  data_opexec = [opexec for (_, opexec, _) in data]
+  data_op = [[opcodes.OpCode.LoadOpCode(op) for op in ops]
+             for (_, _, ops) in data]
+
+  assert compat.all(not op.depends or len(op.depends) == 1
+                    for ops in data_op
+                    for op in ops)
+
+  # Check resolved job IDs in dependencies
+  for (job_idx, res_jobdep) in [(1, data_job_id[0]),
+                                (2, data_job_id[0]),
+                                (4, data_job_id[2])]:
+    if data_op[job_idx][0].depends[0][0] != res_jobdep:
+      raise errors.OpExecError("Job %s's opcode doesn't depend on correct job"
+                               " ID (%s)" % (job_idx, res_jobdep))
+
+  # Check execution order
+  if not (data_opexec[0] <= data_opexec[1] and
+          data_opexec[0] <= data_opexec[2] and
+          data_opexec[2] <= data_opexec[4]):
+    raise errors.OpExecError("Jobs did not run in correct order: %s" % data)
+
+  assert len(jobs) == 5 and compat.all(len(ops) == 1 for ops in jobs)
+
+  ToStdout("Job dependency tests were successful")
+
+
 def _TestJobSubmission(opts):
   """Tests submitting jobs.
 
@@ -210,7 +299,7 @@ def _TestJobSubmission(opts):
       cl.SubmitJob(ops)
     except errors.GenericError, err:
       if opts.debug:
-        ToStdout("Ignoring error: %s", err)
+        ToStdout("Ignoring error for 'wrong priority' test: %s", err)
     else:
       raise errors.OpExecError("Submitting opcode with priority %s did not"
                                " fail when it should (allowed are %s)" %
@@ -225,7 +314,8 @@ def _TestJobSubmission(opts):
     result = cl.SubmitManyJobs(jobs)
     if not (len(result) == 2 and
             compat.all(len(i) == 2 for i in result) and
-            compat.all(isinstance(i[1], basestring) for i in result) and
+            isinstance(result[0][1], int) and
+            isinstance(result[1][1], basestring) and
             result[0][0] and not result[1][0]):
       raise errors.OpExecError("Submitting multiple jobs did not work as"
                                " expected, result %s" % result)
@@ -309,10 +399,10 @@ class _JobQueueTestReporter(cli.StdioJobPollReportCb):
       logging.debug("Status of job %s is %s", job_id, status)
 
       if test == constants.JQT_EXPANDNAMES:
-        if status != constants.JOB_STATUS_WAITLOCK:
+        if status != constants.JOB_STATUS_WAITING:
           raise errors.OpExecError("Job status while expanding names is '%s',"
                                    " not '%s' as expected" %
-                                   (status, constants.JOB_STATUS_WAITLOCK))
+                                   (status, constants.JOB_STATUS_WAITING))
       elif test in (constants.JQT_EXEC, constants.JQT_LOGMSG):
         if status != constants.JOB_STATUS_RUNNING:
           raise errors.OpExecError("Job status while executing opcode is '%s',"
@@ -336,12 +426,18 @@ def TestJobqueue(opts, _):
 
   """
   _TestJobSubmission(opts)
+  _TestJobDependency(opts)
 
   (TM_SUCCESS,
    TM_MULTISUCCESS,
    TM_FAIL,
    TM_PARTFAIL) = range(4)
-  TM_ALL = frozenset([TM_SUCCESS, TM_MULTISUCCESS, TM_FAIL, TM_PARTFAIL])
+  TM_ALL = compat.UniqueFrozenset([
+    TM_SUCCESS,
+    TM_MULTISUCCESS,
+    TM_FAIL,
+    TM_PARTFAIL,
+    ])
 
   for mode in TM_ALL:
     test_messages = [
@@ -404,7 +500,7 @@ def TestJobqueue(opts, _):
         opcodes.OpTestJqueue(notify_waitlock=True,
                              notify_exec=True,
                              log_messages=test_messages,
-                             fail=fail)
+                             fail=fail),
         ]
       expect_messages = [test_messages]
       expect_resultlen = 1
@@ -423,7 +519,7 @@ def TestJobqueue(opts, _):
     except errors.OpExecError, err:
       if not fail:
         raise
-      ToStdout("Ignoring error: %s", err)
+      ToStdout("Ignoring error for 'job fail' test: %s", err)
     else:
       if fail:
         raise errors.OpExecError("Job didn't fail when it should")
@@ -476,7 +572,7 @@ def TestJobqueue(opts, _):
   return 0
 
 
-def ListLocks(opts, args): # pylint: disable-msg=W0613
+def ListLocks(opts, args): # pylint: disable=W0613
   """List all locks.
 
   @param opts: the command line options selected by the user
@@ -527,7 +623,7 @@ def ListLocks(opts, args): # pylint: disable-msg=W0613
 
 
 commands = {
-  'delay': (
+  "delay": (
     Delay, [ArgUnknown(min=1, max=1)],
     [cli_option("--no-master", dest="on_master", default=True,
                 action="store_false", help="Do not sleep in the master code"),
@@ -535,10 +631,10 @@ commands = {
                 action="append", help="Select nodes to sleep on"),
      cli_option("-r", "--repeat", type="int", default="0", dest="repeat",
                 help="Number of times to repeat the sleep"),
-     DRY_RUN_OPT, PRIORITY_OPT,
+     DRY_RUN_OPT, PRIORITY_OPT, SUBMIT_OPT,
      ],
     "[opts...] <duration>", "Executes a TestDelay OpCode"),
-  'submit-job': (
+  "submit-job": (
     GenericOpCodes, [ArgFile(min=1)],
     [VERBOSE_OPT,
      cli_option("--op-repeat", type="int", default="1", dest="rep_op",
@@ -553,7 +649,7 @@ commands = {
      ],
     "<op_list_file...>", "Submits jobs built from json files"
     " containing a list of serialized opcodes"),
-  'iallocator': (
+  "iallocator": (
     TestAllocator, [ArgUnknown(min=1)],
     [cli_option("--dir", dest="direction", default=constants.IALLOCATOR_DIR_IN,
                 choices=list(constants.VALID_IALLOCATOR_DIRECTIONS),
@@ -564,7 +660,7 @@ commands = {
                 choices=list(constants.VALID_IALLOCATOR_MODES),
                 help=("Request mode (one of %s)" %
                       utils.CommaJoin(constants.VALID_IALLOCATOR_MODES))),
-     cli_option("--mem", default=128, type="unit",
+     cli_option("--memory", default=128, type="unit",
                 help="Memory size for the instance (MiB)"),
      cli_option("--disks", default="4096,4096",
                 help="Comma separated list of disk sizes (MiB)"),
@@ -578,11 +674,16 @@ commands = {
                 help="Select number of VCPUs for the instance"),
      cli_option("--tags", default=None,
                 help="Comma separated list of tags"),
-     cli_option("--reloc-mode", default=constants.IALLOCATOR_MRELOC_ANY,
-                choices=list(constants.IALLOCATOR_MRELOC_MODES),
-                help=("Instance relocation mode (one of %s)" %
-                      utils.CommaJoin(constants.IALLOCATOR_MRELOC_MODES))),
-     cli_option("--target-groups", help="Target groups for relocation"),
+     cli_option("--evac-mode", default=constants.IALLOCATOR_NEVAC_ALL,
+                choices=list(constants.IALLOCATOR_NEVAC_MODES),
+                help=("Node evacuation mode (one of %s)" %
+                      utils.CommaJoin(constants.IALLOCATOR_NEVAC_MODES))),
+     cli_option("--target-groups", help="Target groups for relocation",
+                default=[], action="append"),
+     cli_option("--spindle-use", help="How many spindles to use",
+                default=1, type="int"),
+     cli_option("--count", help="How many instances to allocate",
+                default=2, type="int"),
      DRY_RUN_OPT, PRIORITY_OPT,
      ],
     "{opts...} <instance>", "Executes a TestAllocator OpCode"),
@@ -600,5 +701,6 @@ aliases = {
   "allocator": "iallocator",
   }
 
+
 def Main():
   return GenericMain(commands, aliases=aliases)