Revision ab4832d1

b/qa/ganeti-qa.py
484 484
    qa_config.ReleaseNode(node)
485 485

  
486 486

  
487
def _BuildSpecDict(par, mn, st, mx):
488
  return {par: {"min": mn, "std": st, "max": mx}}
489

  
490

  
491
def TestIPolicyPlainInstance():
492
  """Test instance policy interaction with instances"""
493
  params = ["mem-size", "cpu-count", "disk-count", "disk-size", "nic-count"]
494
  if not qa_config.IsTemplateSupported(constants.DT_PLAIN):
495
    print "Template %s not supported" % constants.DT_PLAIN
496
    return
497

  
498
  # This test assumes that the group policy is empty
499
  (_, old_specs) = qa_cluster.TestClusterSetISpecs({})
500
  node = qa_config.AcquireNode()
501
  try:
502
    instance = qa_instance.TestInstanceAddWithPlainDisk([node])
503
    try:
504
      policyerror = [constants.CV_EINSTANCEPOLICY]
505
      for par in params:
506
        qa_cluster.AssertClusterVerify()
507
        (iminval, imaxval) = qa_instance.GetInstanceSpec(instance["name"], par)
508
        # Some specs must be multiple of 4
509
        new_spec = _BuildSpecDict(par, imaxval + 4, imaxval + 4, imaxval + 4)
510
        qa_cluster.TestClusterSetISpecs(new_spec)
511
        qa_cluster.AssertClusterVerify(warnings=policyerror)
512
        if iminval > 0:
513
          # Some specs must be multiple of 4
514
          if iminval >= 4:
515
            upper = iminval - 4
516
          else:
517
            upper = iminval - 1
518
          new_spec = _BuildSpecDict(par, 0, upper, upper)
519
          qa_cluster.TestClusterSetISpecs(new_spec)
520
          qa_cluster.AssertClusterVerify(warnings=policyerror)
521
        qa_cluster.TestClusterSetISpecs(old_specs)
522
      qa_instance.TestInstanceRemove(instance)
523
    finally:
524
      qa_config.ReleaseInstance(instance)
525
  finally:
526
    qa_config.ReleaseNode(node)
527

  
528

  
487 529
def RunInstanceTests():
488 530
  """Create and exercise instances."""
489 531
  instance_tests = [
......
612 654
    qa_config.ReleaseNode(pnode)
613 655

  
614 656
  RunExclusiveStorageTests()
657
  RunTestIf(["cluster-instance-policy", "instance-add-plain-disk"],
658
            TestIPolicyPlainInstance)
615 659

  
616 660
  # Test removing instance with offline drbd secondary
617 661
  if qa_config.TestEnabled("instance-remove-drbd-offline"):
b/qa/qa-sample.json
119 119
    "cluster-redist-conf": true,
120 120
    "cluster-repair-disk-sizes": true,
121 121
    "cluster-exclusive-storage": true,
122
    "cluster-instance-policy": true,
122 123

  
123 124
    "haskell-confd": true,
124 125
    "htools": true,
b/qa/qa_cluster.py
96 96

  
97 97

  
98 98
# Cluster-verify errors (date, "ERROR", then error code)
99
_CVERROR_RE = re.compile(r"^[\w\s:]+\s+- ERROR:([A-Z0-9_-]+):")
99
_CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
100 100

  
101 101

  
102 102
def _GetCVErrorCodes(cvout):
103
  ret = set()
103
  errs = set()
104
  warns = set()
104 105
  for l in cvout.splitlines():
105 106
    m = _CVERROR_RE.match(l)
106 107
    if m:
107
      ecode = m.group(1)
108
      ret.add(ecode)
109
  return ret
108
      etype = m.group(1)
109
      ecode = m.group(2)
110
      if etype == "ERROR":
111
        errs.add(ecode)
112
      elif etype == "WARNING":
113
        warns.add(ecode)
114
  return (errs, warns)
110 115

  
111 116

  
112
def AssertClusterVerify(fail=False, errors=None):
117
def _CheckVerifyErrors(actual, expected, etype):
118
  exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
119
  if not actual.issuperset(exp_codes):
120
    missing = exp_codes.difference(actual)
121
    raise qa_error.Error("Cluster-verify didn't return these expected"
122
                         " %ss: %s" % (etype, utils.CommaJoin(missing)))
123

  
124

  
125
def AssertClusterVerify(fail=False, errors=None, warnings=None):
113 126
  """Run cluster-verify and check the result
114 127

  
115 128
  @type fail: bool
......
118 131
  @param errors: List of CV_XXX errors that are expected; if specified, all the
119 132
      errors listed must appear in cluster-verify output. A non-empty value
120 133
      implies C{fail=True}.
134
  @type warnings: list of tuples
135
  @param warnings: Same as C{errors} but for warnings.
121 136

  
122 137
  """
123 138
  cvcmd = "gnt-cluster verify"
124 139
  mnode = qa_config.GetMasterNode()
125
  if errors:
140
  if errors or warnings:
126 141
    cvout = GetCommandOutput(mnode["primary"], cvcmd + " --error-codes",
127
                             fail=True)
128
    actual = _GetCVErrorCodes(cvout)
129
    expected = compat.UniqueFrozenset(e for (_, e, _) in errors)
130
    if not actual.issuperset(expected):
131
      missing = expected.difference(actual)
132
      raise qa_error.Error("Cluster-verify didn't return these expected"
133
                           " errors: %s" % utils.CommaJoin(missing))
142
                             fail=(fail or errors))
143
    (act_errs, act_warns) = _GetCVErrorCodes(cvout)
144
    if errors:
145
      _CheckVerifyErrors(act_errs, errors, "error")
146
    if warnings:
147
      _CheckVerifyErrors(act_warns, warnings, "warning")
134 148
  else:
135 149
    AssertCommand(cvcmd, fail=fail, node=mnode)
136 150

  
b/qa/qa_instance.py
153 153
    AssertCommand(["lvremove", "-f"] + vols, node=node)
154 154

  
155 155

  
156
def _GetBoolInstanceField(instance, field):
157
  """Get the Boolean value of a field of an instance.
156
def _GetInstanceField(instance, field):
157
  """Get the value of a field of an instance.
158 158

  
159 159
  @type instance: string
160 160
  @param instance: Instance name
161 161
  @type field: string
162 162
  @param field: Name of the field
163
  @rtype: string
163 164

  
164 165
  """
165 166
  master = qa_config.GetMasterNode()
166 167
  infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
167
                                  "-o", field, instance])
168
  info_out = qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
168
                                  "--units", "m", "-o", field, instance])
169
  return qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
170

  
171

  
172
def _GetBoolInstanceField(instance, field):
173
  """Get the Boolean value of a field of an instance.
174

  
175
  @type instance: string
176
  @param instance: Instance name
177
  @type field: string
178
  @param field: Name of the field
179
  @rtype: bool
180

  
181
  """
182
  info_out = _GetInstanceField(instance, field)
169 183
  if info_out == "Y":
170 184
    return True
171 185
  elif info_out == "N":
......
175 189
                         " %s" % (field, instance, info_out))
176 190

  
177 191

  
192
def _GetNumInstanceField(instance, field):
193
  """Get a numeric value of a field of an instance.
194

  
195
  @type instance: string
196
  @param instance: Instance name
197
  @type field: string
198
  @param field: Name of the field
199
  @rtype: int or float
200

  
201
  """
202
  info_out = _GetInstanceField(instance, field)
203
  try:
204
    ret = int(info_out)
205
  except ValueError:
206
    try:
207
      ret = float(info_out)
208
    except ValueError:
209
      raise qa_error.Error("Field %s of instance %s has a non-numeric value:"
210
                           " %s" % (field, instance, info_out))
211
  return ret
212

  
213

  
214
def GetInstanceSpec(instance, spec):
215
  """Return the current spec for the given parameter.
216

  
217
  @type instance: string
218
  @param instance: Instance name
219
  @type spec: string
220
  @param spec: one of the supported parameters: "mem-size", "cpu-count",
221
      "disk-count", "disk-size", "nic-count"
222
  @rtype: tuple
223
  @return: (minspec, maxspec); minspec and maxspec can be different only for
224
      memory and disk size
225

  
226
  """
227
  specmap = {
228
    "mem-size": ["be/minmem", "be/maxmem"],
229
    "cpu-count": ["vcpus"],
230
    "disk-count": ["disk.count"],
231
    "disk-size": ["disk.size/ "],
232
    "nic-count": ["nic.count"],
233
    }
234
  # For disks, first we need the number of disks
235
  if spec == "disk-size":
236
    (numdisk, _) = GetInstanceSpec(instance, "disk-count")
237
    fields = ["disk.size/%s" % k for k in range(0, numdisk)]
238
  else:
239
    assert spec in specmap, "%s not in %s" % (spec, specmap)
240
    fields = specmap[spec]
241
  values = [_GetNumInstanceField(instance, f) for f in fields]
242
  return (min(values), max(values))
243

  
244

  
178 245
def IsFailoverSupported(instance):
179 246
  templ = qa_config.GetInstanceTemplate(instance)
180 247
  return templ in constants.DTS_MIRRORED

Also available in: Unified diff