Revision d61df03e

b/lib/cmdlib.py
30 30
import tempfile
31 31
import re
32 32
import platform
33
import simplejson
33 34

  
34 35
from ganeti import rpc
35 36
from ganeti import ssh
......
44 45
from ganeti import ssconf
45 46

  
46 47

  
48
# Check whether the simplejson module supports indentation
49
_JSON_INDENT = 2
50
try:
51
  simplejson.dumps(1, indent=_JSON_INDENT)
52
except TypeError:
53
  _JSON_INDENT = None
54

  
55

  
47 56
class LogicalUnit(object):
48 57
  """Logical Unit base class.
49 58

  
......
4639 4648
        if not node_result:
4640 4649
          raise errors.OpExecError("Failure during rpc call to node %s,"
4641 4650
                                   " result: %s" % (node, node_result))
4651

  
4652

  
4653
def _AllocatorGetClusterData(cfg, sstore):
4654
  """Compute the generic allocator input data.
4655

  
4656
  This is the data that is independent of the actual operation.
4657

  
4658
  """
4659
  # cluster data
4660
  data = {
4661
    "version": 1,
4662
    "cluster_name": sstore.GetClusterName(),
4663
    "cluster_tags": list(cfg.GetClusterInfo().GetTags()),
4664
    # we don't have job IDs
4665
    }
4666

  
4667
  # node data
4668
  node_results = {}
4669
  node_list = cfg.GetNodeList()
4670
  node_data = rpc.call_node_info(node_list, cfg.GetVGName())
4671
  for nname in node_list:
4672
    ninfo = cfg.GetNodeInfo(nname)
4673
    if nname not in node_data or not isinstance(node_data[nname], dict):
4674
      raise errors.OpExecError("Can't get data for node %s" % nname)
4675
    remote_info = node_data[nname]
4676
    for attr in ['memory_total', 'memory_free',
4677
                 'vg_size', 'vg_free']:
4678
      if attr not in remote_info:
4679
        raise errors.OpExecError("Node '%s' didn't return attribute '%s'" %
4680
                                 (nname, attr))
4681
      try:
4682
        int(remote_info[attr])
4683
      except ValueError, err:
4684
        raise errors.OpExecError("Node '%s' returned invalid value for '%s':"
4685
                                 " %s" % (nname, attr, str(err)))
4686
    pnr = {
4687
      "tags": list(ninfo.GetTags()),
4688
      "total_memory": utils.TryConvert(int, remote_info['memory_total']),
4689
      "free_memory": utils.TryConvert(int, remote_info['memory_free']),
4690
      "total_disk": utils.TryConvert(int, remote_info['vg_size']),
4691
      "free_disk": utils.TryConvert(int, remote_info['vg_free']),
4692
      "primary_ip": ninfo.primary_ip,
4693
      "secondary_ip": ninfo.secondary_ip,
4694
      }
4695
    node_results[nname] = pnr
4696
  data["nodes"] = node_results
4697

  
4698
  # instance data
4699
  instance_data = {}
4700
  i_list = cfg.GetInstanceList()
4701
  for iname in i_list:
4702
    iinfo = cfg.GetInstanceInfo(iname)
4703
    nic_data = [{"mac": n.mac, "ip": n.ip, "bridge": n.bridge}
4704
                for n in iinfo.nics]
4705
    pir = {
4706
      "tags": list(iinfo.GetTags()),
4707
      "should_run": iinfo.status == "up",
4708
      "vcpus": iinfo.vcpus,
4709
      "memory": iinfo.memory,
4710
      "os": iinfo.os,
4711
      "nodes": [iinfo.primary_node] + list(iinfo.secondary_nodes),
4712
      "nics": nic_data,
4713
      "disks": [{"size": dsk.size, "mode": "w"} for dsk in iinfo.disks],
4714
      "disk_template": iinfo.disk_template,
4715
      }
4716
    instance_data[iname] = pir
4717

  
4718
  data["instances"] = instance_data
4719

  
4720
  return data
4721

  
4722

  
4723
def _AllocatorAddNewInstance(data, op):
4724
  """Add new instance data to allocator structure.
4725

  
4726
  This in combination with _AllocatorGetClusterData will create the
4727
  correct structure needed as input for the allocator.
4728

  
4729
  The checks for the completeness of the opcode must have already been
4730
  done.
4731

  
4732
  """
4733
  request = {
4734
    "type": "allocate",
4735
    "name": op.name,
4736
    "disk_template": op.disk_template,
4737
    "tags": op.tags,
4738
    "os": op.os,
4739
    "vcpus": op.vcpus,
4740
    "memory": op.mem_size,
4741
    "disks": op.disks,
4742
    "nics": op.nics,
4743
    }
4744
  data["request"] = request
4745

  
4746

  
4747
def _AllocatorAddRelocateInstance(data, op):
4748
  """Add relocate instance data to allocator structure.
4749

  
4750
  This in combination with _AllocatorGetClusterData will create the
4751
  correct structure needed as input for the allocator.
4752

  
4753
  The checks for the completeness of the opcode must have already been
4754
  done.
4755

  
4756
  """
4757
  request = {
4758
    "type": "replace_secondary",
4759
    "name": op.name,
4760
    }
4761
  data["request"] = request
4762

  
4763

  
4764
class LUTestAllocator(NoHooksLU):
4765
  """Run allocator tests.
4766

  
4767
  This LU runs the allocator tests
4768

  
4769
  """
4770
  _OP_REQP = ["direction", "mode", "name"]
4771

  
4772
  def CheckPrereq(self):
4773
    """Check prerequisites.
4774

  
4775
    This checks the opcode parameters depending on the director and mode test.
4776

  
4777
    """
4778
    if self.op.mode == constants.ALF_MODE_ALLOC:
4779
      for attr in ["name", "mem_size", "disks", "disk_template",
4780
                   "os", "tags", "nics", "vcpus"]:
4781
        if not hasattr(self.op, attr):
4782
          raise errors.OpPrereqError("Missing attribute '%s' on opcode input" %
4783
                                     attr)
4784
      iname = self.cfg.ExpandInstanceName(self.op.name)
4785
      if iname is not None:
4786
        raise errors.OpPrereqError("Instance '%s' already in the cluster" %
4787
                                   iname)
4788
      if not isinstance(self.op.nics, list):
4789
        raise errors.OpPrereqError("Invalid parameter 'nics'")
4790
      for row in self.op.nics:
4791
        if (not isinstance(row, dict) or
4792
            "mac" not in row or
4793
            "ip" not in row or
4794
            "bridge" not in row):
4795
          raise errors.OpPrereqError("Invalid contents of the"
4796
                                     " 'nics' parameter")
4797
      if not isinstance(self.op.disks, list):
4798
        raise errors.OpPrereqError("Invalid parameter 'disks'")
4799
      for row in self.op.disks:
4800
        if (not isinstance(row, dict) or
4801
            "size" not in row or
4802
            not isinstance(row["size"], int) or
4803
            "mode" not in row or
4804
            row["mode"] not in ['r', 'w']):
4805
          raise errors.OpPrereqError("Invalid contents of the"
4806
                                     " 'disks' parameter")
4807
    elif self.op.mode == constants.ALF_MODE_RELOC:
4808
      if not hasattr(self.op, "name"):
4809
        raise errors.OpPrereqError("Missing attribute 'name' on opcode input")
4810
      fname = self.cfg.ExpandInstanceName(self.op.name)
4811
      if fname is None:
4812
        raise errors.OpPrereqError("Instance '%s' not found for relocation" %
4813
                                   self.op.name)
4814
      self.op.name = fname
4815
    else:
4816
      raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
4817
                                 self.op.mode)
4818

  
4819
    if self.op.direction == constants.ALF_DIR_OUT:
4820
      if not hasattr(self.op, "allocator"):
4821
        raise errors.OpPrereqError("Missing allocator name")
4822
      raise errors.OpPrereqError("Allocator out mode not supported yet")
4823
    elif self.op.direction != constants.ALF_DIR_IN:
4824
      raise errors.OpPrereqError("Wrong allocator test '%s'" %
4825
                                 self.op.direction)
4826

  
4827
  def Exec(self, feedback_fn):
4828
    """Run the allocator test.
4829

  
4830
    """
4831
    data = _AllocatorGetClusterData(self.cfg, self.sstore)
4832
    if self.op.mode == constants.ALF_MODE_ALLOC:
4833
      _AllocatorAddNewInstance(data, self.op)
4834
    else:
4835
      _AllocatorAddRelocateInstance(data, self.op)
4836

  
4837
    if _JSON_INDENT is None:
4838
      text = simplejson.dumps(data)
4839
    else:
4840
      text = simplejson.dumps(data, indent=_JSON_INDENT)
4841
    return text
b/lib/constants.py
184 184
VERIFY_NPLUSONE_MEM = 'nplusone_mem'
185 185
VERIFY_OPTIONAL_CHECKS = frozenset([VERIFY_NPLUSONE_MEM])
186 186

  
187
# Allocator framework constants
188
ALF_DIR_IN = "in"
189
ALF_DIR_OUT = "out"
190
ALF_MODE_ALLOC = "allocate"
191
ALF_MODE_RELOC = "relocate"
b/lib/mcpu.py
87 87
    opcodes.OpDelTags: cmdlib.LUDelTags,
88 88
    # test lu
89 89
    opcodes.OpTestDelay: cmdlib.LUTestDelay,
90
    opcodes.OpTestAllocator: cmdlib.LUTestAllocator,
90 91
    }
91 92

  
92 93
  def __init__(self, feedback=None):
b/lib/opcodes.py
444 444
  """
445 445
  OP_ID = "OP_TEST_DELAY"
446 446
  __slots__ = ["duration", "on_master", "on_nodes"]
447

  
448

  
449
class OpTestAllocator(OpCode):
450
  """Allocator framework testing.
451

  
452
  This opcode has two modes:
453
    - gather and return allocator input for a given mode (allocate new
454
      or replace secondary) and a given instance definition (direction
455
      'in')
456
    - run a selected allocator for a given operation (as above) and
457
      return the allocator output (direction 'out')
458

  
459
  """
460
  OP_ID = "OP_TEST_ALLOCATOR"
461
  __slots__ = [
462
    "direction", "mode", "allocator", "name",
463
    "mem_size", "disks", "disk_template",
464
    "os", "tags", "nics", "vcpus",
465
    ]
b/scripts/gnt-debug
97 97
  return 0
98 98

  
99 99

  
100
def TestAllocator(opts, args):
101
  """Runs the test allocator opcode"""
102

  
103
  try:
104
    disks = [{"size": utils.ParseUnit(val), "mode": 'w'}
105
             for val in opts.disks.split(",")]
106
  except errors.UnitParseError, err:
107
    print >> sys.stderr, "Invalid disks parameter '%s': %s" % (opts.disks, err)
108
    return 1
109

  
110
  nics = [val.split("/") for val in opts.nics.split(",")]
111
  for row in nics:
112
    while len(row) < 3:
113
      row.append(None)
114
    for i in range(3):
115
      if row[i] == '':
116
        row[i] = None
117
  nic_dict = [{"mac": v[0], "ip": v[1], "bridge": v[2]} for v in nics]
118

  
119
  if opts.tags is None:
120
    opts.tags = []
121
  else:
122
    opts.tags = opts.tags.split(",")
123

  
124
  op = opcodes.OpTestAllocator(mode=opts.mode,
125
                               name=args[0],
126
                               mem_size=opts.mem,
127
                               disks=disks,
128
                               disk_template=opts.disk_template,
129
                               nics=nic_dict,
130
                               os=opts.os_type,
131
                               vcpus=opts.vcpus,
132
                               tags=opts.tags,
133
                               direction=opts.direction,
134
                               allocator=opts.allocator,
135
                               )
136
  result = SubmitOpCode(op)
137
  print result
138
  return 0
139

  
140

  
100 141
commands = {
101 142
  'delay': (Delay, ARGS_ONE,
102 143
            [DEBUG_OPT,
......
113 154
                  ],
114 155
                 "<op_list_file>", "Submits a job built from a json-file"
115 156
                 " with a list of serialized opcodes"),
157
  'allocator': (TestAllocator, ARGS_ONE,
158
                [DEBUG_OPT,
159
                 make_option("--dir", dest="direction",
160
                             default="in", choices=["in", "out"],
161
                             help="Show allocator input (in) or allocator"
162
                             " results (out)"),
163
                 make_option("--algorithm", dest="allocator",
164
                             default=None,
165
                             help="Allocator algorithm name"),
166
                 make_option("-m", "--mode", default="relocate",
167
                             choices=["relocate", "allocate"],
168
                             help="Request mode, either allocate or"
169
                             "relocate"),
170
                 cli_option("--mem", default=128, type="unit",
171
                            help="Memory size for the instance (MiB)"),
172
                 make_option("--disks", default="4096,4096",
173
                             help="Comma separated list of disk sizes (MiB)"),
174
                 make_option("-t", "--disk-template", default="drbd",
175
                             help="Select the disk template"),
176
                 make_option("--nics", default="00:11:22:33:44:55",
177
                             help="Comma separated list of nics, each nic"
178
                             " definition is of form mac/ip/bridge, if"
179
                             " missing values are replace by None"),
180
                 make_option("-o", "--os-type", default=None,
181
                             help="Select os for the instance"),
182
                 make_option("-p", "--vcpus", default=1, type="int",
183
                             help="Select number of VCPUs for the instance"),
184
                 make_option("--tags", default=None,
185
                             help="Comma separated list of tags"),
186
                 ],
187
                "{opts...} <instance>", "Executes a TestAllocator OpCode"),
116 188
  }
117 189

  
118 190

  

Also available in: Unified diff