QA fix: TestInstanceExportWithRemove() updates configuration
[ganeti-local] / qa / qa_cluster.py
1 #
2 #
3
4 # Copyright (C) 2007, 2010, 2011, 2012, 2013 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
22 """Cluster related QA tests.
23
24 """
25
26 import re
27 import tempfile
28 import os.path
29
30 from ganeti import constants
31 from ganeti import compat
32 from ganeti import utils
33 from ganeti import pathutils
34
35 import qa_config
36 import qa_utils
37 import qa_error
38
39 from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
40
41
42 # Prefix for LVM volumes created by QA code during tests
43 _QA_LV_PREFIX = "qa-"
44
45 #: cluster verify command
46 _CLUSTER_VERIFY = ["gnt-cluster", "verify"]
47
48
49 def _RemoveFileFromAllNodes(filename):
50   """Removes a file from all nodes.
51
52   """
53   for node in qa_config.get("nodes"):
54     AssertCommand(["rm", "-f", filename], node=node)
55
56
57 def _CheckFileOnAllNodes(filename, content):
58   """Verifies the content of the given file on all nodes.
59
60   """
61   cmd = utils.ShellQuoteArgs(["cat", filename])
62   for node in qa_config.get("nodes"):
63     AssertEqual(qa_utils.GetCommandOutput(node["primary"], cmd), content)
64
65
66 # "gnt-cluster info" fields
67 _CIFIELD_RE = re.compile(r"^[-\s]*(?P<field>[^\s:]+):\s*(?P<value>\S.*)$")
68
69
70 def _GetBoolClusterField(field):
71   """Get the Boolean value of a cluster field.
72
73   This function currently assumes that the field name is unique in the cluster
74   configuration. An assertion checks this assumption.
75
76   @type field: string
77   @param field: Name of the field
78   @rtype: bool
79   @return: The effective value of the field
80
81   """
82   master = qa_config.GetMasterNode()
83   infocmd = "gnt-cluster info"
84   info_out = qa_utils.GetCommandOutput(master["primary"], infocmd)
85   ret = None
86   for l in info_out.splitlines():
87     m = _CIFIELD_RE.match(l)
88     # FIXME: There should be a way to specify a field through a hierarchy
89     if m and m.group("field") == field:
90       # Make sure that ignoring the hierarchy doesn't cause a double match
91       assert ret is None
92       ret = (m.group("value").lower() == "true")
93   if ret is not None:
94     return ret
95   raise qa_error.Error("Field not found in cluster configuration: %s" % field)
96
97
98 # Cluster-verify errors (date, "ERROR", then error code)
99 _CVERROR_RE = re.compile(r"^[\w\s:]+\s+- ERROR:([A-Z0-9_-]+):")
100
101
102 def _GetCVErrorCodes(cvout):
103   ret = set()
104   for l in cvout.splitlines():
105     m = _CVERROR_RE.match(l)
106     if m:
107       ecode = m.group(1)
108       ret.add(ecode)
109   return ret
110
111
112 def AssertClusterVerify(fail=False, errors=None):
113   """Run cluster-verify and check the result
114
115   @type fail: bool
116   @param fail: if cluster-verify is expected to fail instead of succeeding
117   @type errors: list of tuples
118   @param errors: List of CV_XXX errors that are expected; if specified, all the
119       errors listed must appear in cluster-verify output. A non-empty value
120       implies C{fail=True}.
121
122   """
123   cvcmd = "gnt-cluster verify"
124   mnode = qa_config.GetMasterNode()
125   if errors:
126     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))
134   else:
135     AssertCommand(cvcmd, fail=fail, node=mnode)
136
137
138 # data for testing failures due to bad keys/values for disk parameters
139 _FAIL_PARAMS = ["nonexistent:resync-rate=1",
140                 "drbd:nonexistent=1",
141                 "drbd:resync-rate=invalid",
142                 ]
143
144
145 def TestClusterInitDisk():
146   """gnt-cluster init -D"""
147   name = qa_config.get("name")
148   for param in _FAIL_PARAMS:
149     AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
150
151
152 def TestClusterInit(rapi_user, rapi_secret):
153   """gnt-cluster init"""
154   master = qa_config.GetMasterNode()
155
156   rapi_dir = os.path.dirname(pathutils.RAPI_USERS_FILE)
157
158   # First create the RAPI credentials
159   fh = tempfile.NamedTemporaryFile()
160   try:
161     fh.write("%s %s write\n" % (rapi_user, rapi_secret))
162     fh.flush()
163
164     tmpru = qa_utils.UploadFile(master["primary"], fh.name)
165     try:
166       AssertCommand(["mkdir", "-p", rapi_dir])
167       AssertCommand(["mv", tmpru, pathutils.RAPI_USERS_FILE])
168     finally:
169       AssertCommand(["rm", "-f", tmpru])
170   finally:
171     fh.close()
172
173   # Initialize cluster
174   cmd = [
175     "gnt-cluster", "init",
176     "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
177     "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
178     ]
179
180   for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
181                     "nic-count"):
182     for spec_val in ("min", "max", "std"):
183       spec = qa_config.get("ispec_%s_%s" %
184                            (spec_type.replace("-", "_"), spec_val), None)
185       if spec:
186         cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
187
188   if master.get("secondary", None):
189     cmd.append("--secondary-ip=%s" % master["secondary"])
190
191   vgname = qa_config.get("vg-name", None)
192   if vgname:
193     cmd.append("--vg-name=%s" % vgname)
194
195   master_netdev = qa_config.get("master-netdev", None)
196   if master_netdev:
197     cmd.append("--master-netdev=%s" % master_netdev)
198
199   nicparams = qa_config.get("default-nicparams", None)
200   if nicparams:
201     cmd.append("--nic-parameters=%s" %
202                ",".join(utils.FormatKeyValue(nicparams)))
203
204   cmd.append(qa_config.get("name"))
205   AssertCommand(cmd)
206
207   cmd = ["gnt-cluster", "modify"]
208
209   # hypervisor parameter modifications
210   hvp = qa_config.get("hypervisor-parameters", {})
211   for k, v in hvp.items():
212     cmd.extend(["-H", "%s:%s" % (k, v)])
213   # backend parameter modifications
214   bep = qa_config.get("backend-parameters", "")
215   if bep:
216     cmd.extend(["-B", bep])
217
218   if len(cmd) > 2:
219     AssertCommand(cmd)
220
221   # OS parameters
222   osp = qa_config.get("os-parameters", {})
223   for k, v in osp.items():
224     AssertCommand(["gnt-os", "modify", "-O", v, k])
225
226   # OS hypervisor parameters
227   os_hvp = qa_config.get("os-hvp", {})
228   for os_name in os_hvp:
229     for hv, hvp in os_hvp[os_name].items():
230       AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
231
232
233 def TestClusterRename():
234   """gnt-cluster rename"""
235   cmd = ["gnt-cluster", "rename", "-f"]
236
237   original_name = qa_config.get("name")
238   rename_target = qa_config.get("rename", None)
239   if rename_target is None:
240     print qa_utils.FormatError('"rename" entry is missing')
241     return
242
243   for data in [
244     cmd + [rename_target],
245     _CLUSTER_VERIFY,
246     cmd + [original_name],
247     _CLUSTER_VERIFY,
248     ]:
249     AssertCommand(data)
250
251
252 def TestClusterOob():
253   """out-of-band framework"""
254   oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
255
256   AssertCommand(_CLUSTER_VERIFY)
257   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
258                  "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
259                  utils.NewUUID()])
260
261   AssertCommand(_CLUSTER_VERIFY, fail=True)
262
263   AssertCommand(["touch", oob_path_exists])
264   AssertCommand(["chmod", "0400", oob_path_exists])
265   AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
266
267   try:
268     AssertCommand(["gnt-cluster", "modify", "--node-parameters",
269                    "oob_program=%s" % oob_path_exists])
270
271     AssertCommand(_CLUSTER_VERIFY, fail=True)
272
273     AssertCommand(["chmod", "0500", oob_path_exists])
274     AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
275
276     AssertCommand(_CLUSTER_VERIFY)
277   finally:
278     AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
279
280   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
281                  "oob_program="])
282
283
284 def TestClusterEpo():
285   """gnt-cluster epo"""
286   master = qa_config.GetMasterNode()
287
288   # Assert that OOB is unavailable for all nodes
289   result_output = GetCommandOutput(master["primary"],
290                                    "gnt-node list --verbose --no-headers -o"
291                                    " powered")
292   AssertEqual(compat.all(powered == "(unavail)"
293                          for powered in result_output.splitlines()), True)
294
295   # Conflicting
296   AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
297   # --all doesn't expect arguments
298   AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
299
300   # Unless --all is given master is not allowed to be in the list
301   AssertCommand(["gnt-cluster", "epo", "-f", master["primary"]], fail=True)
302
303   # This shouldn't fail
304   AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
305
306   # All instances should have been stopped now
307   result_output = GetCommandOutput(master["primary"],
308                                    "gnt-instance list --no-headers -o status")
309   # ERROR_down because the instance is stopped but not recorded as such
310   AssertEqual(compat.all(status == "ERROR_down"
311                          for status in result_output.splitlines()), True)
312
313   # Now start everything again
314   AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
315
316   # All instances should have been started now
317   result_output = GetCommandOutput(master["primary"],
318                                    "gnt-instance list --no-headers -o status")
319   AssertEqual(compat.all(status == "running"
320                          for status in result_output.splitlines()), True)
321
322
323 def TestClusterVerify():
324   """gnt-cluster verify"""
325   AssertCommand(_CLUSTER_VERIFY)
326   AssertCommand(["gnt-cluster", "verify-disks"])
327
328
329 def TestJobqueue():
330   """gnt-debug test-jobqueue"""
331   AssertCommand(["gnt-debug", "test-jobqueue"])
332
333
334 def TestDelay(node):
335   """gnt-debug delay"""
336   AssertCommand(["gnt-debug", "delay", "1"])
337   AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
338   AssertCommand(["gnt-debug", "delay", "--no-master",
339                  "-n", node["primary"], "1"])
340
341
342 def TestClusterReservedLvs():
343   """gnt-cluster reserved lvs"""
344   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
345   lvname = _QA_LV_PREFIX + "test"
346   lvfullname = "/".join([vgname, lvname])
347   for fail, cmd in [
348     (False, _CLUSTER_VERIFY),
349     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
350     (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
351     (True, _CLUSTER_VERIFY),
352     (False, ["gnt-cluster", "modify", "--reserved-lvs",
353              "%s,.*/other-test" % lvfullname]),
354     (False, _CLUSTER_VERIFY),
355     (False, ["gnt-cluster", "modify", "--reserved-lvs",
356              ".*/%s.*" % _QA_LV_PREFIX]),
357     (False, _CLUSTER_VERIFY),
358     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
359     (True, _CLUSTER_VERIFY),
360     (False, ["lvremove", "-f", lvfullname]),
361     (False, _CLUSTER_VERIFY),
362     ]:
363     AssertCommand(cmd, fail=fail)
364
365
366 def TestClusterModifyEmpty():
367   """gnt-cluster modify"""
368   AssertCommand(["gnt-cluster", "modify"], fail=True)
369
370
371 def TestClusterModifyDisk():
372   """gnt-cluster modify -D"""
373   for param in _FAIL_PARAMS:
374     AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
375
376
377 def TestClusterModifyBe():
378   """gnt-cluster modify -B"""
379   for fail, cmd in [
380     # max/min mem
381     (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
382     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
383     (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
384     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
385     (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
386     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
387     (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
388     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
389     (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
390     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
391     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
392     # vcpus
393     (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
394     (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
395     (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
396     (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
397     (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
398     # auto_balance
399     (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
400     (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
401     (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
402     (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
403     (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
404     ]:
405     AssertCommand(cmd, fail=fail)
406
407   # redo the original-requested BE parameters, if any
408   bep = qa_config.get("backend-parameters", "")
409   if bep:
410     AssertCommand(["gnt-cluster", "modify", "-B", bep])
411
412
413 def TestClusterInfo():
414   """gnt-cluster info"""
415   AssertCommand(["gnt-cluster", "info"])
416
417
418 def TestClusterRedistConf():
419   """gnt-cluster redist-conf"""
420   AssertCommand(["gnt-cluster", "redist-conf"])
421
422
423 def TestClusterGetmaster():
424   """gnt-cluster getmaster"""
425   AssertCommand(["gnt-cluster", "getmaster"])
426
427
428 def TestClusterVersion():
429   """gnt-cluster version"""
430   AssertCommand(["gnt-cluster", "version"])
431
432
433 def TestClusterRenewCrypto():
434   """gnt-cluster renew-crypto"""
435   master = qa_config.GetMasterNode()
436
437   # Conflicting options
438   cmd = ["gnt-cluster", "renew-crypto", "--force",
439          "--new-cluster-certificate", "--new-confd-hmac-key"]
440   conflicting = [
441     ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
442     ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
443     ]
444   for i in conflicting:
445     AssertCommand(cmd + i, fail=True)
446
447   # Invalid RAPI certificate
448   cmd = ["gnt-cluster", "renew-crypto", "--force",
449          "--rapi-certificate=/dev/null"]
450   AssertCommand(cmd, fail=True)
451
452   rapi_cert_backup = qa_utils.BackupFile(master["primary"],
453                                          pathutils.RAPI_CERT_FILE)
454   try:
455     # Custom RAPI certificate
456     fh = tempfile.NamedTemporaryFile()
457
458     # Ensure certificate doesn't cause "gnt-cluster verify" to complain
459     validity = constants.SSL_CERT_EXPIRATION_WARN * 3
460
461     utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
462
463     tmpcert = qa_utils.UploadFile(master["primary"], fh.name)
464     try:
465       AssertCommand(["gnt-cluster", "renew-crypto", "--force",
466                      "--rapi-certificate=%s" % tmpcert])
467     finally:
468       AssertCommand(["rm", "-f", tmpcert])
469
470     # Custom cluster domain secret
471     cds_fh = tempfile.NamedTemporaryFile()
472     cds_fh.write(utils.GenerateSecret())
473     cds_fh.write("\n")
474     cds_fh.flush()
475
476     tmpcds = qa_utils.UploadFile(master["primary"], cds_fh.name)
477     try:
478       AssertCommand(["gnt-cluster", "renew-crypto", "--force",
479                      "--cluster-domain-secret=%s" % tmpcds])
480     finally:
481       AssertCommand(["rm", "-f", tmpcds])
482
483     # Normal case
484     AssertCommand(["gnt-cluster", "renew-crypto", "--force",
485                    "--new-cluster-certificate", "--new-confd-hmac-key",
486                    "--new-rapi-certificate", "--new-cluster-domain-secret"])
487
488     # Restore RAPI certificate
489     AssertCommand(["gnt-cluster", "renew-crypto", "--force",
490                    "--rapi-certificate=%s" % rapi_cert_backup])
491   finally:
492     AssertCommand(["rm", "-f", rapi_cert_backup])
493
494
495 def TestClusterBurnin():
496   """Burnin"""
497   master = qa_config.GetMasterNode()
498
499   options = qa_config.get("options", {})
500   disk_template = options.get("burnin-disk-template", "drbd")
501   parallel = options.get("burnin-in-parallel", False)
502   check_inst = options.get("burnin-check-instances", False)
503   do_rename = options.get("burnin-rename", "")
504   do_reboot = options.get("burnin-reboot", True)
505   reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
506
507   # Get as many instances as we need
508   instances = []
509   try:
510     try:
511       num = qa_config.get("options", {}).get("burnin-instances", 1)
512       for _ in range(0, num):
513         instances.append(qa_config.AcquireInstance())
514     except qa_error.OutOfInstancesError:
515       print "Not enough instances, continuing anyway."
516
517     if len(instances) < 1:
518       raise qa_error.Error("Burnin needs at least one instance")
519
520     script = qa_utils.UploadFile(master["primary"], "../tools/burnin")
521     try:
522       # Run burnin
523       cmd = [script,
524              "--os=%s" % qa_config.get("os"),
525              "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
526              "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
527              "--disk-size=%s" % ",".join(qa_config.get("disk")),
528              "--disk-growth=%s" % ",".join(qa_config.get("disk-growth")),
529              "--disk-template=%s" % disk_template]
530       if parallel:
531         cmd.append("--parallel")
532         cmd.append("--early-release")
533       if check_inst:
534         cmd.append("--http-check")
535       if do_rename:
536         cmd.append("--rename=%s" % do_rename)
537       if not do_reboot:
538         cmd.append("--no-reboot")
539       else:
540         cmd.append("--reboot-types=%s" % ",".join(reboot_types))
541       cmd += [inst["name"] for inst in instances]
542       AssertCommand(cmd)
543     finally:
544       AssertCommand(["rm", "-f", script])
545
546   finally:
547     for inst in instances:
548       qa_config.ReleaseInstance(inst)
549
550
551 def TestClusterMasterFailover():
552   """gnt-cluster master-failover"""
553   master = qa_config.GetMasterNode()
554   failovermaster = qa_config.AcquireNode(exclude=master)
555
556   cmd = ["gnt-cluster", "master-failover"]
557   try:
558     AssertCommand(cmd, node=failovermaster)
559     # Back to original master node
560     AssertCommand(cmd, node=master)
561   finally:
562     qa_config.ReleaseNode(failovermaster)
563
564
565 def TestClusterMasterFailoverWithDrainedQueue():
566   """gnt-cluster master-failover with drained queue"""
567   drain_check = ["test", "-f", pathutils.JOB_QUEUE_DRAIN_FILE]
568
569   master = qa_config.GetMasterNode()
570   failovermaster = qa_config.AcquireNode(exclude=master)
571
572   # Ensure queue is not drained
573   for node in [master, failovermaster]:
574     AssertCommand(drain_check, node=node, fail=True)
575
576   # Drain queue on failover master
577   AssertCommand(["touch", pathutils.JOB_QUEUE_DRAIN_FILE], node=failovermaster)
578
579   cmd = ["gnt-cluster", "master-failover"]
580   try:
581     AssertCommand(drain_check, node=failovermaster)
582     AssertCommand(cmd, node=failovermaster)
583     AssertCommand(drain_check, fail=True)
584     AssertCommand(drain_check, node=failovermaster, fail=True)
585
586     # Back to original master node
587     AssertCommand(cmd, node=master)
588   finally:
589     qa_config.ReleaseNode(failovermaster)
590
591   AssertCommand(drain_check, fail=True)
592   AssertCommand(drain_check, node=failovermaster, fail=True)
593
594
595 def TestClusterCopyfile():
596   """gnt-cluster copyfile"""
597   master = qa_config.GetMasterNode()
598
599   uniqueid = utils.NewUUID()
600
601   # Create temporary file
602   f = tempfile.NamedTemporaryFile()
603   f.write(uniqueid)
604   f.flush()
605   f.seek(0)
606
607   # Upload file to master node
608   testname = qa_utils.UploadFile(master["primary"], f.name)
609   try:
610     # Copy file to all nodes
611     AssertCommand(["gnt-cluster", "copyfile", testname])
612     _CheckFileOnAllNodes(testname, uniqueid)
613   finally:
614     _RemoveFileFromAllNodes(testname)
615
616
617 def TestClusterCommand():
618   """gnt-cluster command"""
619   uniqueid = utils.NewUUID()
620   rfile = "/tmp/gnt%s" % utils.NewUUID()
621   rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
622   cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
623                               "%s >%s" % (rcmd, rfile)])
624
625   try:
626     AssertCommand(cmd)
627     _CheckFileOnAllNodes(rfile, uniqueid)
628   finally:
629     _RemoveFileFromAllNodes(rfile)
630
631
632 def TestClusterDestroy():
633   """gnt-cluster destroy"""
634   AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
635
636
637 def TestClusterRepairDiskSizes():
638   """gnt-cluster repair-disk-sizes"""
639   AssertCommand(["gnt-cluster", "repair-disk-sizes"])
640
641
642 def TestSetExclStorCluster(newvalue):
643   """Set the exclusive_storage node parameter at the cluster level.
644
645   @type newvalue: bool
646   @param newvalue: New value of exclusive_storage
647   @rtype: bool
648   @return: The old value of exclusive_storage
649
650   """
651   oldvalue = _GetBoolClusterField("exclusive_storage")
652   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
653                  "exclusive_storage=%s" % newvalue])
654   effvalue = _GetBoolClusterField("exclusive_storage")
655   if effvalue != newvalue:
656     raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
657                          " of %s" % (effvalue, newvalue))
658   return oldvalue
659
660
661 def _BuildSetESCmd(value, node_name):
662   return ["gnt-node", "modify", "--node-parameters",
663           "exclusive_storage=%s" % value, node_name]
664
665
666 def TestExclStorSingleNode(node):
667   """cluster-verify reports exclusive_storage set only on one node.
668
669   """
670   node_name = node["primary"]
671   es_val = _GetBoolClusterField("exclusive_storage")
672   assert not es_val
673   AssertCommand(_BuildSetESCmd(True, node_name))
674   AssertClusterVerify(fail=True, errors=[constants.CV_EGROUPMIXEDESFLAG])
675   AssertCommand(_BuildSetESCmd("default", node_name))
676   AssertClusterVerify()
677
678
679 def TestExclStorSharedPv(node):
680   """cluster-verify reports LVs that share the same PV with exclusive_storage.
681
682   """
683   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
684   lvname1 = _QA_LV_PREFIX + "vol1"
685   lvname2 = _QA_LV_PREFIX + "vol2"
686   node_name = node["primary"]
687   AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
688   AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
689   AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
690   AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
691                                          constants.CV_ENODEORPHANLV])
692   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
693   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
694   AssertClusterVerify()