QA: adjust tests wrt to ipolicy disk templates
[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_daemon
37 import qa_utils
38 import qa_error
39 import qa_instance
40
41 from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
42
43
44 # Prefix for LVM volumes created by QA code during tests
45 _QA_LV_PREFIX = "qa-"
46
47 #: cluster verify command
48 _CLUSTER_VERIFY = ["gnt-cluster", "verify"]
49
50
51 def _RemoveFileFromAllNodes(filename):
52   """Removes a file from all nodes.
53
54   """
55   for node in qa_config.get("nodes"):
56     AssertCommand(["rm", "-f", filename], node=node)
57
58
59 def _CheckFileOnAllNodes(filename, content):
60   """Verifies the content of the given file on all nodes.
61
62   """
63   cmd = utils.ShellQuoteArgs(["cat", filename])
64   for node in qa_config.get("nodes"):
65     AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
66
67
68 def _GetClusterField(field_path):
69   """Get the value of a cluster field.
70
71   @type field_path: list of strings
72   @param field_path: Names of the groups/fields to navigate to get the desired
73       value, e.g. C{["Default node parameters", "oob_program"]}
74   @return: The effective value of the field (the actual type depends on the
75       chosen field)
76
77   """
78   assert isinstance(field_path, list)
79   assert field_path
80   ret = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
81   for key in field_path:
82     ret = ret[key]
83   return ret
84
85
86 # Cluster-verify errors (date, "ERROR", then error code)
87 _CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
88
89
90 def _GetCVErrorCodes(cvout):
91   errs = set()
92   warns = set()
93   for l in cvout.splitlines():
94     m = _CVERROR_RE.match(l)
95     if m:
96       etype = m.group(1)
97       ecode = m.group(2)
98       if etype == "ERROR":
99         errs.add(ecode)
100       elif etype == "WARNING":
101         warns.add(ecode)
102   return (errs, warns)
103
104
105 def _CheckVerifyErrors(actual, expected, etype):
106   exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
107   if not actual.issuperset(exp_codes):
108     missing = exp_codes.difference(actual)
109     raise qa_error.Error("Cluster-verify didn't return these expected"
110                          " %ss: %s" % (etype, utils.CommaJoin(missing)))
111
112
113 def AssertClusterVerify(fail=False, errors=None, warnings=None):
114   """Run cluster-verify and check the result
115
116   @type fail: bool
117   @param fail: if cluster-verify is expected to fail instead of succeeding
118   @type errors: list of tuples
119   @param errors: List of CV_XXX errors that are expected; if specified, all the
120       errors listed must appear in cluster-verify output. A non-empty value
121       implies C{fail=True}.
122   @type warnings: list of tuples
123   @param warnings: Same as C{errors} but for warnings.
124
125   """
126   cvcmd = "gnt-cluster verify"
127   mnode = qa_config.GetMasterNode()
128   if errors or warnings:
129     cvout = GetCommandOutput(mnode.primary, cvcmd + " --error-codes",
130                              fail=(fail or errors))
131     (act_errs, act_warns) = _GetCVErrorCodes(cvout)
132     if errors:
133       _CheckVerifyErrors(act_errs, errors, "error")
134     if warnings:
135       _CheckVerifyErrors(act_warns, warnings, "warning")
136   else:
137     AssertCommand(cvcmd, fail=fail, node=mnode)
138
139
140 # data for testing failures due to bad keys/values for disk parameters
141 _FAIL_PARAMS = ["nonexistent:resync-rate=1",
142                 "drbd:nonexistent=1",
143                 "drbd:resync-rate=invalid",
144                 ]
145
146
147 def TestClusterInitDisk():
148   """gnt-cluster init -D"""
149   name = qa_config.get("name")
150   for param in _FAIL_PARAMS:
151     AssertCommand(["gnt-cluster", "init", "-D", param, name], fail=True)
152
153
154 def TestClusterInit(rapi_user, rapi_secret):
155   """gnt-cluster init"""
156   master = qa_config.GetMasterNode()
157
158   rapi_users_path = qa_utils.MakeNodePath(master, pathutils.RAPI_USERS_FILE)
159   rapi_dir = os.path.dirname(rapi_users_path)
160
161   # First create the RAPI credentials
162   fh = tempfile.NamedTemporaryFile()
163   try:
164     fh.write("%s %s write\n" % (rapi_user, rapi_secret))
165     fh.flush()
166
167     tmpru = qa_utils.UploadFile(master.primary, fh.name)
168     try:
169       AssertCommand(["mkdir", "-p", rapi_dir])
170       AssertCommand(["mv", tmpru, rapi_users_path])
171     finally:
172       AssertCommand(["rm", "-f", tmpru])
173   finally:
174     fh.close()
175
176   # Initialize cluster
177   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
178   cmd = [
179     "gnt-cluster", "init",
180     "--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
181     "--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
182     "--enabled-disk-templates=%s" %
183       ",".join(enabled_disk_templates),
184     ]
185   if constants.DT_FILE in enabled_disk_templates:
186     cmd.append(
187         "--file-storage-dir=%s" %
188         qa_config.get("default-file-storage-dir",
189                       pathutils.DEFAULT_FILE_STORAGE_DIR))
190
191   for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
192                     "nic-count"):
193     for spec_val in ("min", "max", "std"):
194       spec = qa_config.get("ispec_%s_%s" %
195                            (spec_type.replace("-", "_"), spec_val), None)
196       if spec is not None:
197         cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
198
199   if master.secondary:
200     cmd.append("--secondary-ip=%s" % master.secondary)
201
202   if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
203     vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
204     if vgname:
205       cmd.append("--vg-name=%s" % vgname)
206     else:
207       raise qa_error.Error("Please specify a volume group if you enable"
208                            " lvm-based disk templates in the QA.")
209
210   master_netdev = qa_config.get("master-netdev", None)
211   if master_netdev:
212     cmd.append("--master-netdev=%s" % master_netdev)
213
214   nicparams = qa_config.get("default-nicparams", None)
215   if nicparams:
216     cmd.append("--nic-parameters=%s" %
217                ",".join(utils.FormatKeyValue(nicparams)))
218
219   # Cluster value of the exclusive-storage node parameter
220   e_s = qa_config.get("exclusive-storage")
221   if e_s is not None:
222     cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
223   else:
224     e_s = False
225   qa_config.SetExclusiveStorage(e_s)
226
227   extra_args = qa_config.get("cluster-init-args")
228   if extra_args:
229     cmd.extend(extra_args)
230
231   cmd.append(qa_config.get("name"))
232
233   AssertCommand(cmd)
234
235   cmd = ["gnt-cluster", "modify"]
236
237   # hypervisor parameter modifications
238   hvp = qa_config.get("hypervisor-parameters", {})
239   for k, v in hvp.items():
240     cmd.extend(["-H", "%s:%s" % (k, v)])
241   # backend parameter modifications
242   bep = qa_config.get("backend-parameters", "")
243   if bep:
244     cmd.extend(["-B", bep])
245
246   if len(cmd) > 2:
247     AssertCommand(cmd)
248
249   # OS parameters
250   osp = qa_config.get("os-parameters", {})
251   for k, v in osp.items():
252     AssertCommand(["gnt-os", "modify", "-O", v, k])
253
254   # OS hypervisor parameters
255   os_hvp = qa_config.get("os-hvp", {})
256   for os_name in os_hvp:
257     for hv, hvp in os_hvp[os_name].items():
258       AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
259
260
261 def TestClusterRename():
262   """gnt-cluster rename"""
263   cmd = ["gnt-cluster", "rename", "-f"]
264
265   original_name = qa_config.get("name")
266   rename_target = qa_config.get("rename", None)
267   if rename_target is None:
268     print qa_utils.FormatError('"rename" entry is missing')
269     return
270
271   for data in [
272     cmd + [rename_target],
273     _CLUSTER_VERIFY,
274     cmd + [original_name],
275     _CLUSTER_VERIFY,
276     ]:
277     AssertCommand(data)
278
279
280 def TestClusterOob():
281   """out-of-band framework"""
282   oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
283
284   AssertCommand(_CLUSTER_VERIFY)
285   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
286                  "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
287                  utils.NewUUID()])
288
289   AssertCommand(_CLUSTER_VERIFY, fail=True)
290
291   AssertCommand(["touch", oob_path_exists])
292   AssertCommand(["chmod", "0400", oob_path_exists])
293   AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
294
295   try:
296     AssertCommand(["gnt-cluster", "modify", "--node-parameters",
297                    "oob_program=%s" % oob_path_exists])
298
299     AssertCommand(_CLUSTER_VERIFY, fail=True)
300
301     AssertCommand(["chmod", "0500", oob_path_exists])
302     AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
303
304     AssertCommand(_CLUSTER_VERIFY)
305   finally:
306     AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
307
308   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
309                  "oob_program="])
310
311
312 def TestClusterEpo():
313   """gnt-cluster epo"""
314   master = qa_config.GetMasterNode()
315
316   # Assert that OOB is unavailable for all nodes
317   result_output = GetCommandOutput(master.primary,
318                                    "gnt-node list --verbose --no-headers -o"
319                                    " powered")
320   AssertEqual(compat.all(powered == "(unavail)"
321                          for powered in result_output.splitlines()), True)
322
323   # Conflicting
324   AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
325   # --all doesn't expect arguments
326   AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
327
328   # Unless --all is given master is not allowed to be in the list
329   AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
330
331   # This shouldn't fail
332   AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
333
334   # All instances should have been stopped now
335   result_output = GetCommandOutput(master.primary,
336                                    "gnt-instance list --no-headers -o status")
337   # ERROR_down because the instance is stopped but not recorded as such
338   AssertEqual(compat.all(status == "ERROR_down"
339                          for status in result_output.splitlines()), True)
340
341   # Now start everything again
342   AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
343
344   # All instances should have been started now
345   result_output = GetCommandOutput(master.primary,
346                                    "gnt-instance list --no-headers -o status")
347   AssertEqual(compat.all(status == "running"
348                          for status in result_output.splitlines()), True)
349
350
351 def TestClusterVerify():
352   """gnt-cluster verify"""
353   AssertCommand(_CLUSTER_VERIFY)
354   AssertCommand(["gnt-cluster", "verify-disks"])
355
356
357 def TestClusterVerifyDisksBrokenDRBD(instance, inst_nodes):
358   """gnt-cluster verify-disks with broken DRBD"""
359   qa_daemon.TestPauseWatcher()
360
361   try:
362     info = qa_instance.GetInstanceInfo(instance.name)
363     snode = inst_nodes[1]
364     for idx, minor in enumerate(info["drbd-minors"][snode.primary]):
365       if idx % 2 == 0:
366         break_drbd_cmd = \
367           "(drbdsetup %d down >/dev/null 2>&1;" \
368           " drbdsetup down resource%d >/dev/null 2>&1) || /bin/true" % \
369           (minor, minor)
370       else:
371         break_drbd_cmd = \
372           "(drbdsetup %d detach >/dev/null 2>&1;" \
373           " drbdsetup detach %d >/dev/null 2>&1) || /bin/true" % \
374           (minor, minor)
375       AssertCommand(break_drbd_cmd, node=snode)
376
377     verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
378                                      "gnt-cluster verify-disks")
379     activation_msg = "Activating disks for instance '%s'" % instance.name
380     if activation_msg not in verify_output:
381       raise qa_error.Error("gnt-cluster verify-disks did not activate broken"
382                            " DRBD disks:\n%s" % verify_output)
383
384     verify_output = GetCommandOutput(qa_config.GetMasterNode().primary,
385                                      "gnt-cluster verify-disks")
386     if activation_msg in verify_output:
387       raise qa_error.Error("gnt-cluster verify-disks wants to activate broken"
388                            " DRBD disks on second attempt:\n%s" % verify_output)
389
390     AssertCommand(_CLUSTER_VERIFY)
391   finally:
392     qa_daemon.TestResumeWatcher()
393
394
395 def TestJobqueue():
396   """gnt-debug test-jobqueue"""
397   AssertCommand(["gnt-debug", "test-jobqueue"])
398
399
400 def TestDelay(node):
401   """gnt-debug delay"""
402   AssertCommand(["gnt-debug", "delay", "1"])
403   AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
404   AssertCommand(["gnt-debug", "delay", "--no-master",
405                  "-n", node.primary, "1"])
406
407
408 def TestClusterReservedLvs():
409   """gnt-cluster reserved lvs"""
410   # if no lvm-based templates are supported, skip the test
411   if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
412     return
413   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
414   lvname = _QA_LV_PREFIX + "test"
415   lvfullname = "/".join([vgname, lvname])
416   for fail, cmd in [
417     (False, _CLUSTER_VERIFY),
418     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
419     (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
420     (True, _CLUSTER_VERIFY),
421     (False, ["gnt-cluster", "modify", "--reserved-lvs",
422              "%s,.*/other-test" % lvfullname]),
423     (False, _CLUSTER_VERIFY),
424     (False, ["gnt-cluster", "modify", "--reserved-lvs",
425              ".*/%s.*" % _QA_LV_PREFIX]),
426     (False, _CLUSTER_VERIFY),
427     (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
428     (True, _CLUSTER_VERIFY),
429     (False, ["lvremove", "-f", lvfullname]),
430     (False, _CLUSTER_VERIFY),
431     ]:
432     AssertCommand(cmd, fail=fail)
433
434
435 def TestClusterModifyEmpty():
436   """gnt-cluster modify"""
437   AssertCommand(["gnt-cluster", "modify"], fail=True)
438
439
440 def TestClusterModifyDisk():
441   """gnt-cluster modify -D"""
442   for param in _FAIL_PARAMS:
443     AssertCommand(["gnt-cluster", "modify", "-D", param], fail=True)
444
445
446 def _GetOtherEnabledDiskTemplate(undesired_disk_templates,
447                                  enabled_disk_templates):
448   """Returns one template that is not in the undesired set.
449
450   @type undesired_disk_templates: list of string
451   @param undesired_disk_templates: a list of disk templates that we want to
452       exclude when drawing one disk template from the list of enabled
453       disk templates
454   @type enabled_disk_templates: list of string
455   @param enabled_disk_templates: list of enabled disk templates (in QA)
456
457   """
458   desired_templates = list(set(enabled_disk_templates)
459                                 - set(undesired_disk_templates))
460   if desired_templates:
461     template = desired_templates[0]
462   else:
463     # If no desired disk template is available for QA, choose 'diskless' and
464     # hope for the best.
465     template = constants.ST_DISKLESS
466
467   return template
468
469
470 def TestClusterModifyFileBasedStorageDir(
471     file_disk_template, dir_config_key, default_dir, option_name):
472   """Tests gnt-cluster modify wrt to file-based directory options.
473
474   @type file_disk_template: string
475   @param file_disk_template: file-based disk template
476   @type dir_config_key: string
477   @param dir_config_key: key for the QA config to retrieve the default
478      directory value
479   @type default_dir: string
480   @param default_dir: default directory, if the QA config does not specify
481      it
482   @type option_name: string
483   @param option_name: name of the option of 'gnt-cluster modify' to
484      change the directory
485
486   """
487   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
488   assert file_disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]
489   if not qa_config.IsTemplateSupported(file_disk_template):
490     return
491
492   # Get some non-file-based disk template to disable file storage
493   other_disk_template = _GetOtherEnabledDiskTemplate(
494       utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE),
495       enabled_disk_templates)
496
497   file_storage_dir = qa_config.get(dir_config_key, default_dir)
498   invalid_file_storage_dir = "/boot/"
499
500   for fail, cmd in [
501     (False, ["gnt-cluster", "modify",
502             "--enabled-disk-templates=%s" % file_disk_template]),
503     (False, ["gnt-cluster", "modify",
504             "--%s=%s" % (option_name, file_storage_dir)]),
505     (False, ["gnt-cluster", "modify",
506             "--%s=%s" % (option_name, invalid_file_storage_dir)]),
507     # file storage dir is set to an inacceptable path, thus verify
508     # should fail
509     (True, ["gnt-cluster", "verify"]),
510     # unsetting the storage dir while file storage is enabled
511     # should fail
512     (True, ["gnt-cluster", "modify",
513             "--%s=" % option_name]),
514     (False, ["gnt-cluster", "modify",
515             "--%s=%s" % (option_name, file_storage_dir)]),
516     (False, ["gnt-cluster", "modify",
517             "--enabled-disk-templates=%s" % other_disk_template]),
518     (False, ["gnt-cluster", "modify",
519             "--%s=%s" % (option_name, invalid_file_storage_dir)]),
520     # file storage is set to an inacceptable path, but file storage
521     # is disabled, thus verify should not fail
522     (False, ["gnt-cluster", "verify"]),
523     # unsetting the file storage dir while file storage is not enabled
524     # should be fine
525     (False, ["gnt-cluster", "modify",
526             "--%s=" % option_name]),
527     # resetting everything to sane values
528     (False, ["gnt-cluster", "modify",
529             "--%s=%s" % (option_name, file_storage_dir),
530             "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates)])
531     ]:
532     AssertCommand(cmd, fail=fail)
533
534
535 def TestClusterModifyFileStorageDir():
536   """gnt-cluster modify --file-storage-dir=..."""
537   TestClusterModifyFileBasedStorageDir(
538       constants.DT_FILE, "default-file-storage-dir",
539       pathutils.DEFAULT_FILE_STORAGE_DIR,
540       "file-storage-dir")
541
542
543 def TestClusterModifySharedFileStorageDir():
544   """gnt-cluster modify --shared-file-storage-dir=..."""
545   TestClusterModifyFileBasedStorageDir(
546       constants.DT_SHARED_FILE, "default-shared-file-storage-dir",
547       pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
548       "shared-file-storage-dir")
549
550
551 def TestClusterModifyDiskTemplates():
552   """gnt-cluster modify --enabled-disk-templates=..."""
553   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
554   default_disk_template = qa_config.GetDefaultDiskTemplate()
555
556   _TestClusterModifyDiskTemplatesArguments(default_disk_template,
557                                            enabled_disk_templates)
558   _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates)
559
560   _RestoreEnabledDiskTemplates()
561   nodes = qa_config.AcquireManyNodes(2)
562
563   instance_template = enabled_disk_templates[0]
564   instance = qa_instance.CreateInstanceByDiskTemplate(nodes, instance_template)
565
566   _TestClusterModifyUnusedDiskTemplate(instance_template)
567   _TestClusterModifyUsedDiskTemplate(instance_template,
568                                      enabled_disk_templates)
569
570   qa_instance.TestInstanceRemove(instance)
571   _RestoreEnabledDiskTemplates()
572
573
574 def _RestoreEnabledDiskTemplates():
575   """Sets the list of enabled disk templates back to the list of enabled disk
576      templates from the QA configuration. This can be used to make sure that
577      the tests that modify the list of disk templates do not interfere with
578      other tests.
579
580   """
581   enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
582   cmd = ["gnt-cluster", "modify",
583          "--enabled-disk-templates=%s" % ",".join(enabled_disk_templates),
584          "--ipolicy-disk-templates=%s" % ",".join(enabled_disk_templates),
585          ]
586
587   if utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
588     vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
589     cmd.append("--vg-name=%s" % vgname)
590
591   AssertCommand(cmd, fail=False)
592
593
594 def _TestClusterModifyDiskTemplatesArguments(default_disk_template,
595                                              enabled_disk_templates):
596   """Tests argument handling of 'gnt-cluster modify' with respect to
597      the parameter '--enabled-disk-templates'. This test is independent
598      of instances.
599
600   """
601   _RestoreEnabledDiskTemplates()
602
603   # bogus templates
604   AssertCommand(["gnt-cluster", "modify",
605                  "--enabled-disk-templates=pinkbunny"],
606                 fail=True)
607
608   # duplicate entries do no harm
609   AssertCommand(
610     ["gnt-cluster", "modify",
611      "--enabled-disk-templates=%s,%s" %
612       (default_disk_template, default_disk_template),
613      "--ipolicy-disk-templates=%s" % default_disk_template],
614     fail=False)
615
616   if constants.DT_DRBD8 in enabled_disk_templates:
617     # interaction with --drbd-usermode-helper option
618     drbd_usermode_helper = qa_config.get("drbd-usermode-helper", None)
619     if not drbd_usermode_helper:
620       drbd_usermode_helper = "/bin/true"
621     # specifying a helper when drbd gets disabled is ok. Note that drbd still
622     # has to be installed on the nodes in this case
623     AssertCommand(["gnt-cluster", "modify",
624                    "--drbd-usermode-helper=%s" % drbd_usermode_helper,
625                    "--enabled-disk-templates=%s" % constants.DT_DISKLESS,
626                    "--ipolicy-disk-templates=%s" % constants.DT_DISKLESS],
627                    fail=False)
628     # specifying a helper when drbd is re-enabled
629     AssertCommand(["gnt-cluster", "modify",
630                    "--drbd-usermode-helper=%s" % drbd_usermode_helper,
631                    "--enabled-disk-templates=%s" %
632                      ",".join(enabled_disk_templates),
633                    "--ipolicy-disk-templates=%s" %
634                      ",".join(enabled_disk_templates)],
635                   fail=False)
636
637
638 def _TestClusterModifyDiskTemplatesVgName(enabled_disk_templates):
639   """Tests argument handling of 'gnt-cluster modify' with respect to
640      the parameter '--enabled-disk-templates' and '--vg-name'. This test is
641      independent of instances.
642
643   """
644   if not utils.IsLvmEnabled(enabled_disk_templates):
645     # These tests only make sense if lvm is enabled for QA
646     return
647
648   # determine an LVM and a non-LVM disk template for the tests
649   non_lvm_template = _GetOtherEnabledDiskTemplate(utils.GetLvmDiskTemplates(),
650                                                   enabled_disk_templates)
651   lvm_template = list(set(enabled_disk_templates)
652                       .intersection(set(utils.GetLvmDiskTemplates())))[0]
653
654   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
655
656   # Clean start: unset volume group name, disable lvm storage
657   AssertCommand(
658     ["gnt-cluster", "modify",
659      "--enabled-disk-templates=%s" % non_lvm_template,
660      "--ipolicy-disk-templates=%s" % non_lvm_template,
661      "--vg-name="],
662     fail=False)
663
664   # Try to enable lvm, when no volume group is given
665   AssertCommand(
666     ["gnt-cluster", "modify",
667      "--enabled-disk-templates=%s" % lvm_template,
668      "--ipolicy-disk-templates=%s" % lvm_template],
669     fail=True)
670
671   # Set volume group, with lvm still disabled: just a warning
672   AssertCommand(["gnt-cluster", "modify", "--vg-name=%s" % vgname], fail=False)
673
674   # Try unsetting vg name and enabling lvm at the same time
675   AssertCommand(
676     ["gnt-cluster", "modify",
677      "--enabled-disk-templates=%s" % lvm_template,
678      "--ipolicy-disk-templates=%s" % lvm_template,
679      "--vg-name="],
680     fail=True)
681
682   # Enable lvm with vg name present
683   AssertCommand(
684     ["gnt-cluster", "modify",
685      "--enabled-disk-templates=%s" % lvm_template,
686      "--ipolicy-disk-templates=%s" % lvm_template],
687     fail=False)
688
689   # Try unsetting vg name with lvm still enabled
690   AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=True)
691
692   # Disable lvm with vg name still set
693   AssertCommand(
694     ["gnt-cluster", "modify",
695      "--enabled-disk-templates=%s" % non_lvm_template,
696      "--ipolicy-disk-templates=%s" % non_lvm_template,
697      ],
698     fail=False)
699
700   # Try unsetting vg name with lvm disabled
701   AssertCommand(["gnt-cluster", "modify", "--vg-name="], fail=False)
702
703   # Set vg name and enable lvm at the same time
704   AssertCommand(
705     ["gnt-cluster", "modify",
706      "--enabled-disk-templates=%s" % lvm_template,
707      "--ipolicy-disk-templates=%s" % lvm_template,
708      "--vg-name=%s" % vgname],
709     fail=False)
710
711   # Unset vg name and disable lvm at the same time
712   AssertCommand(
713     ["gnt-cluster", "modify",
714      "--enabled-disk-templates=%s" % non_lvm_template,
715      "--ipolicy-disk-templates=%s" % non_lvm_template,
716      "--vg-name="],
717     fail=False)
718
719   _RestoreEnabledDiskTemplates()
720
721
722 def _TestClusterModifyUsedDiskTemplate(instance_template,
723                                        enabled_disk_templates):
724   """Tests that disk templates that are currently in use by instances cannot
725      be disabled on the cluster.
726
727   """
728   # If the list of enabled disk templates contains only one template
729   # we need to add some other templates, because the list of enabled disk
730   # templates can only be set to a non-empty list.
731   new_disk_templates = list(set(enabled_disk_templates)
732                               - set([instance_template]))
733   if not new_disk_templates:
734     new_disk_templates = list(set([constants.DT_DISKLESS, constants.DT_BLOCK])
735                                 - set([instance_template]))
736   AssertCommand(
737     ["gnt-cluster", "modify",
738      "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
739      "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
740     fail=True)
741
742
743 def _TestClusterModifyUnusedDiskTemplate(instance_template):
744   """Tests that unused disk templates can be disabled safely."""
745   all_disk_templates = constants.DISK_TEMPLATES
746   if not utils.IsLvmEnabled(qa_config.GetEnabledDiskTemplates()):
747     all_disk_templates = list(set(all_disk_templates) -
748                               set(utils.GetLvmDiskTemplates()))
749
750   AssertCommand(
751     ["gnt-cluster", "modify",
752      "--enabled-disk-templates=%s" % ",".join(all_disk_templates),
753      "--ipolicy-disk-templates=%s" % ",".join(all_disk_templates)],
754     fail=False)
755   new_disk_templates = [instance_template]
756   AssertCommand(
757     ["gnt-cluster", "modify",
758      "--enabled-disk-templates=%s" % ",".join(new_disk_templates),
759      "--ipolicy-disk-templates=%s" % ",".join(new_disk_templates)],
760     fail=False)
761
762
763 def TestClusterModifyBe():
764   """gnt-cluster modify -B"""
765   for fail, cmd in [
766     # max/min mem
767     (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
768     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
769     (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
770     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
771     (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
772     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
773     (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
774     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
775     (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
776     (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
777     (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
778     # vcpus
779     (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
780     (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
781     (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
782     (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
783     (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
784     # auto_balance
785     (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
786     (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
787     (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
788     (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
789     (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
790     ]:
791     AssertCommand(cmd, fail=fail)
792
793   # redo the original-requested BE parameters, if any
794   bep = qa_config.get("backend-parameters", "")
795   if bep:
796     AssertCommand(["gnt-cluster", "modify", "-B", bep])
797
798
799 def _GetClusterIPolicy():
800   """Return the run-time values of the cluster-level instance policy.
801
802   @rtype: tuple
803   @return: (policy, specs), where:
804       - policy is a dictionary of the policy values, instance specs excluded
805       - specs is a dictionary containing only the specs, using the internal
806         format (see L{constants.IPOLICY_DEFAULTS} for an example)
807
808   """
809   info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
810   policy = info["Instance policy - limits for instances"]
811   (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
812
813   # Sanity checks
814   assert "minmax" in ret_specs and "std" in ret_specs
815   assert len(ret_specs["minmax"]) > 0
816   assert len(ret_policy) > 0
817   return (ret_policy, ret_specs)
818
819
820 def TestClusterModifyIPolicy():
821   """gnt-cluster modify --ipolicy-*"""
822   basecmd = ["gnt-cluster", "modify"]
823   (old_policy, old_specs) = _GetClusterIPolicy()
824   for par in ["vcpu-ratio", "spindle-ratio"]:
825     curr_val = float(old_policy[par])
826     test_values = [
827       (True, 1.0),
828       (True, 1.5),
829       (True, 2),
830       (False, "a"),
831       # Restore the old value
832       (True, curr_val),
833       ]
834     for (good, val) in test_values:
835       cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
836       AssertCommand(cmd, fail=not good)
837       if good:
838         curr_val = val
839       # Check the affected parameter
840       (eff_policy, eff_specs) = _GetClusterIPolicy()
841       AssertEqual(float(eff_policy[par]), curr_val)
842       # Check everything else
843       AssertEqual(eff_specs, old_specs)
844       for p in eff_policy.keys():
845         if p == par:
846           continue
847         AssertEqual(eff_policy[p], old_policy[p])
848
849   # Disk templates are treated slightly differently
850   par = "disk-templates"
851   disp_str = "allowed disk templates"
852   curr_val = old_policy[disp_str]
853   test_values = [
854     (True, constants.DT_PLAIN),
855     (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
856     (False, "thisisnotadisktemplate"),
857     (False, ""),
858     # Restore the old value
859     (True, curr_val.replace(" ", "")),
860     ]
861   for (good, val) in test_values:
862     cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
863     AssertCommand(cmd, fail=not good)
864     if good:
865       curr_val = val
866     # Check the affected parameter
867     (eff_policy, eff_specs) = _GetClusterIPolicy()
868     AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
869     # Check everything else
870     AssertEqual(eff_specs, old_specs)
871     for p in eff_policy.keys():
872       if p == disp_str:
873         continue
874       AssertEqual(eff_policy[p], old_policy[p])
875
876
877 def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
878                          old_values=None):
879   """Change instance specs.
880
881   At most one of new_specs or diff_specs can be specified.
882
883   @type new_specs: dict
884   @param new_specs: new complete specs, in the same format returned by
885       L{_GetClusterIPolicy}
886   @type diff_specs: dict
887   @param diff_specs: partial specs, it can be an incomplete specifications, but
888       if min/max specs are specified, their number must match the number of the
889       existing specs
890   @type fail: bool
891   @param fail: if the change is expected to fail
892   @type old_values: tuple
893   @param old_values: (old_policy, old_specs), as returned by
894       L{_GetClusterIPolicy}
895   @return: same as L{_GetClusterIPolicy}
896
897   """
898   build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
899   return qa_utils.TestSetISpecs(
900     new_specs=new_specs, diff_specs=diff_specs,
901     get_policy_fn=_GetClusterIPolicy, build_cmd_fn=build_cmd,
902     fail=fail, old_values=old_values)
903
904
905 def TestClusterModifyISpecs():
906   """gnt-cluster modify --specs-*"""
907   params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
908   (cur_policy, cur_specs) = _GetClusterIPolicy()
909   # This test assumes that there is only one min/max bound
910   assert len(cur_specs[constants.ISPECS_MINMAX]) == 1
911   for par in params:
912     test_values = [
913       (True, 0, 4, 12),
914       (True, 4, 4, 12),
915       (True, 4, 12, 12),
916       (True, 4, 4, 4),
917       (False, 4, 0, 12),
918       (False, 4, 16, 12),
919       (False, 4, 4, 0),
920       (False, 12, 4, 4),
921       (False, 12, 4, 0),
922       (False, "a", 4, 12),
923       (False, 0, "a", 12),
924       (False, 0, 4, "a"),
925       # This is to restore the old values
926       (True,
927        cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MIN][par],
928        cur_specs[constants.ISPECS_STD][par],
929        cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX][par])
930       ]
931     for (good, mn, st, mx) in test_values:
932       new_vals = {
933         constants.ISPECS_MINMAX: [{
934           constants.ISPECS_MIN: {par: mn},
935           constants.ISPECS_MAX: {par: mx}
936           }],
937         constants.ISPECS_STD: {par: st}
938         }
939       cur_state = (cur_policy, cur_specs)
940       # We update cur_specs, as we've copied the values to restore already
941       (cur_policy, cur_specs) = TestClusterSetISpecs(
942         diff_specs=new_vals, fail=not good, old_values=cur_state)
943
944     # Get the ipolicy command
945     mnode = qa_config.GetMasterNode()
946     initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
947     modcmd = ["gnt-cluster", "modify"]
948     opts = initcmd.split()
949     assert opts[0:2] == ["gnt-cluster", "init"]
950     for k in range(2, len(opts) - 1):
951       if opts[k].startswith("--ipolicy-"):
952         assert k + 2 <= len(opts)
953         modcmd.extend(opts[k:k + 2])
954     # Re-apply the ipolicy (this should be a no-op)
955     AssertCommand(modcmd)
956     new_initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
957     AssertEqual(initcmd, new_initcmd)
958
959
960 def TestClusterInfo():
961   """gnt-cluster info"""
962   AssertCommand(["gnt-cluster", "info"])
963
964
965 def TestClusterRedistConf():
966   """gnt-cluster redist-conf"""
967   AssertCommand(["gnt-cluster", "redist-conf"])
968
969
970 def TestClusterGetmaster():
971   """gnt-cluster getmaster"""
972   AssertCommand(["gnt-cluster", "getmaster"])
973
974
975 def TestClusterVersion():
976   """gnt-cluster version"""
977   AssertCommand(["gnt-cluster", "version"])
978
979
980 def TestClusterRenewCrypto():
981   """gnt-cluster renew-crypto"""
982   master = qa_config.GetMasterNode()
983
984   # Conflicting options
985   cmd = ["gnt-cluster", "renew-crypto", "--force",
986          "--new-cluster-certificate", "--new-confd-hmac-key"]
987   conflicting = [
988     ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
989     ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
990     ]
991   for i in conflicting:
992     AssertCommand(cmd + i, fail=True)
993
994   # Invalid RAPI certificate
995   cmd = ["gnt-cluster", "renew-crypto", "--force",
996          "--rapi-certificate=/dev/null"]
997   AssertCommand(cmd, fail=True)
998
999   rapi_cert_backup = qa_utils.BackupFile(master.primary,
1000                                          pathutils.RAPI_CERT_FILE)
1001   try:
1002     # Custom RAPI certificate
1003     fh = tempfile.NamedTemporaryFile()
1004
1005     # Ensure certificate doesn't cause "gnt-cluster verify" to complain
1006     validity = constants.SSL_CERT_EXPIRATION_WARN * 3
1007
1008     utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
1009
1010     tmpcert = qa_utils.UploadFile(master.primary, fh.name)
1011     try:
1012       AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1013                      "--rapi-certificate=%s" % tmpcert])
1014     finally:
1015       AssertCommand(["rm", "-f", tmpcert])
1016
1017     # Custom cluster domain secret
1018     cds_fh = tempfile.NamedTemporaryFile()
1019     cds_fh.write(utils.GenerateSecret())
1020     cds_fh.write("\n")
1021     cds_fh.flush()
1022
1023     tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
1024     try:
1025       AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1026                      "--cluster-domain-secret=%s" % tmpcds])
1027     finally:
1028       AssertCommand(["rm", "-f", tmpcds])
1029
1030     # Normal case
1031     AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1032                    "--new-cluster-certificate", "--new-confd-hmac-key",
1033                    "--new-rapi-certificate", "--new-cluster-domain-secret"])
1034
1035     # Restore RAPI certificate
1036     AssertCommand(["gnt-cluster", "renew-crypto", "--force",
1037                    "--rapi-certificate=%s" % rapi_cert_backup])
1038   finally:
1039     AssertCommand(["rm", "-f", rapi_cert_backup])
1040
1041
1042 def TestClusterBurnin():
1043   """Burnin"""
1044   master = qa_config.GetMasterNode()
1045
1046   options = qa_config.get("options", {})
1047   disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
1048   parallel = options.get("burnin-in-parallel", False)
1049   check_inst = options.get("burnin-check-instances", False)
1050   do_rename = options.get("burnin-rename", "")
1051   do_reboot = options.get("burnin-reboot", True)
1052   reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
1053
1054   # Get as many instances as we need
1055   instances = []
1056   try:
1057     try:
1058       num = qa_config.get("options", {}).get("burnin-instances", 1)
1059       for _ in range(0, num):
1060         instances.append(qa_config.AcquireInstance())
1061     except qa_error.OutOfInstancesError:
1062       print "Not enough instances, continuing anyway."
1063
1064     if len(instances) < 1:
1065       raise qa_error.Error("Burnin needs at least one instance")
1066
1067     script = qa_utils.UploadFile(master.primary, "../tools/burnin")
1068     try:
1069       disks = qa_config.GetDiskOptions()
1070       # Run burnin
1071       cmd = [script,
1072              "--os=%s" % qa_config.get("os"),
1073              "--minmem-size=%s" % qa_config.get(constants.BE_MINMEM),
1074              "--maxmem-size=%s" % qa_config.get(constants.BE_MAXMEM),
1075              "--disk-size=%s" % ",".join([d.get("size") for d in disks]),
1076              "--disk-growth=%s" % ",".join([d.get("growth") for d in disks]),
1077              "--disk-template=%s" % disk_template]
1078       if parallel:
1079         cmd.append("--parallel")
1080         cmd.append("--early-release")
1081       if check_inst:
1082         cmd.append("--http-check")
1083       if do_rename:
1084         cmd.append("--rename=%s" % do_rename)
1085       if not do_reboot:
1086         cmd.append("--no-reboot")
1087       else:
1088         cmd.append("--reboot-types=%s" % ",".join(reboot_types))
1089       cmd += [inst.name for inst in instances]
1090       AssertCommand(cmd)
1091     finally:
1092       AssertCommand(["rm", "-f", script])
1093
1094   finally:
1095     for inst in instances:
1096       inst.Release()
1097
1098
1099 def TestClusterMasterFailover():
1100   """gnt-cluster master-failover"""
1101   master = qa_config.GetMasterNode()
1102   failovermaster = qa_config.AcquireNode(exclude=master)
1103
1104   cmd = ["gnt-cluster", "master-failover"]
1105   try:
1106     AssertCommand(cmd, node=failovermaster)
1107     # Back to original master node
1108     AssertCommand(cmd, node=master)
1109   finally:
1110     failovermaster.Release()
1111
1112
1113 def _NodeQueueDrainFile(node):
1114   """Returns path to queue drain file for a node.
1115
1116   """
1117   return qa_utils.MakeNodePath(node, pathutils.JOB_QUEUE_DRAIN_FILE)
1118
1119
1120 def _AssertDrainFile(node, **kwargs):
1121   """Checks for the queue drain file.
1122
1123   """
1124   AssertCommand(["test", "-f", _NodeQueueDrainFile(node)], node=node, **kwargs)
1125
1126
1127 def TestClusterMasterFailoverWithDrainedQueue():
1128   """gnt-cluster master-failover with drained queue"""
1129   master = qa_config.GetMasterNode()
1130   failovermaster = qa_config.AcquireNode(exclude=master)
1131
1132   # Ensure queue is not drained
1133   for node in [master, failovermaster]:
1134     _AssertDrainFile(node, fail=True)
1135
1136   # Drain queue on failover master
1137   AssertCommand(["touch", _NodeQueueDrainFile(failovermaster)],
1138                 node=failovermaster)
1139
1140   cmd = ["gnt-cluster", "master-failover"]
1141   try:
1142     _AssertDrainFile(failovermaster)
1143     AssertCommand(cmd, node=failovermaster)
1144     _AssertDrainFile(master, fail=True)
1145     _AssertDrainFile(failovermaster, fail=True)
1146
1147     # Back to original master node
1148     AssertCommand(cmd, node=master)
1149   finally:
1150     failovermaster.Release()
1151
1152   # Ensure queue is not drained
1153   for node in [master, failovermaster]:
1154     _AssertDrainFile(node, fail=True)
1155
1156
1157 def TestClusterCopyfile():
1158   """gnt-cluster copyfile"""
1159   master = qa_config.GetMasterNode()
1160
1161   uniqueid = utils.NewUUID()
1162
1163   # Create temporary file
1164   f = tempfile.NamedTemporaryFile()
1165   f.write(uniqueid)
1166   f.flush()
1167   f.seek(0)
1168
1169   # Upload file to master node
1170   testname = qa_utils.UploadFile(master.primary, f.name)
1171   try:
1172     # Copy file to all nodes
1173     AssertCommand(["gnt-cluster", "copyfile", testname])
1174     _CheckFileOnAllNodes(testname, uniqueid)
1175   finally:
1176     _RemoveFileFromAllNodes(testname)
1177
1178
1179 def TestClusterCommand():
1180   """gnt-cluster command"""
1181   uniqueid = utils.NewUUID()
1182   rfile = "/tmp/gnt%s" % utils.NewUUID()
1183   rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
1184   cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
1185                               "%s >%s" % (rcmd, rfile)])
1186
1187   try:
1188     AssertCommand(cmd)
1189     _CheckFileOnAllNodes(rfile, uniqueid)
1190   finally:
1191     _RemoveFileFromAllNodes(rfile)
1192
1193
1194 def TestClusterDestroy():
1195   """gnt-cluster destroy"""
1196   AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
1197
1198
1199 def TestClusterRepairDiskSizes():
1200   """gnt-cluster repair-disk-sizes"""
1201   AssertCommand(["gnt-cluster", "repair-disk-sizes"])
1202
1203
1204 def TestSetExclStorCluster(newvalue):
1205   """Set the exclusive_storage node parameter at the cluster level.
1206
1207   @type newvalue: bool
1208   @param newvalue: New value of exclusive_storage
1209   @rtype: bool
1210   @return: The old value of exclusive_storage
1211
1212   """
1213   es_path = ["Default node parameters", "exclusive_storage"]
1214   oldvalue = _GetClusterField(es_path)
1215   AssertCommand(["gnt-cluster", "modify", "--node-parameters",
1216                  "exclusive_storage=%s" % newvalue])
1217   effvalue = _GetClusterField(es_path)
1218   if effvalue != newvalue:
1219     raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
1220                          " of %s" % (effvalue, newvalue))
1221   qa_config.SetExclusiveStorage(newvalue)
1222   return oldvalue
1223
1224
1225 def TestExclStorSharedPv(node):
1226   """cluster-verify reports LVs that share the same PV with exclusive_storage.
1227
1228   """
1229   vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
1230   lvname1 = _QA_LV_PREFIX + "vol1"
1231   lvname2 = _QA_LV_PREFIX + "vol2"
1232   node_name = node.primary
1233   AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
1234   AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
1235   AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
1236   AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
1237                                          constants.CV_ENODEORPHANLV])
1238   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
1239   AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
1240   AssertClusterVerify()