4 # Copyright (C) 2007, 2011, 2012, 2013 Google Inc.
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.
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.
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
22 """Instance related QA tests.
29 from ganeti import utils
30 from ganeti import constants
31 from ganeti import query
32 from ganeti import pathutils
38 from qa_utils import AssertIn, AssertCommand, AssertEqual
39 from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG, RETURN_VALUE
42 def _GetDiskStatePath(disk):
43 return "/sys/block/%s/device/state" % disk
46 def _GetGenericAddParameters(inst, disk_template, force_mac=None):
48 params.append("%s=%s,%s=%s" % (constants.BE_MINMEM,
49 qa_config.get(constants.BE_MINMEM),
51 qa_config.get(constants.BE_MAXMEM)))
53 if disk_template != constants.DT_DISKLESS:
54 for idx, size in enumerate(qa_config.get("disk")):
55 params.extend(["--disk", "%s:size=%s" % (idx, size)])
57 # Set static MAC address if configured
61 nic0_mac = inst.GetNicMacAddr(0, None)
64 params.extend(["--net", "0:mac=%s" % nic0_mac])
69 def _DiskTest(node, disk_template, fail=False):
70 instance = qa_config.AcquireInstance()
72 cmd = (["gnt-instance", "add",
73 "--os-type=%s" % qa_config.get("os"),
74 "--disk-template=%s" % disk_template,
76 _GetGenericAddParameters(instance, disk_template))
77 cmd.append(instance.name)
79 AssertCommand(cmd, fail=fail)
82 _CheckSsconfInstanceList(instance.name)
83 instance.SetDiskTemplate(disk_template)
90 # Handle the case where creation is expected to fail
96 def _GetInstanceInfo(instance):
97 """Return information about the actual state of an instance.
99 @type instance: string
100 @param instance: the instance name
101 @return: a dictionary with the following keys:
102 - "nodes": instance nodes, a list of strings
103 - "volumes": instance volume IDs, a list of strings
104 - "drbd-minors": DRBD minors used by the instance, a dictionary where
105 keys are nodes, and values are lists of integers (or an empty
106 dictionary for non-DRBD instances)
109 master = qa_config.GetMasterNode()
110 infocmd = utils.ShellQuoteArgs(["gnt-instance", "info", instance])
111 info_out = qa_utils.GetCommandOutput(master.primary, infocmd)
112 re_node = re.compile(r"^\s+-\s+(?:primary|secondaries):\s+(\S.+)$")
113 node_elem = r"([^,()]+)(?:\s+\([^)]+\))?"
114 # re_nodelist matches a list of nodes returned by gnt-instance info, e.g.:
116 # node2.fqdn,node3.fqdn
117 # node4.fqdn (group mygroup, group UUID 01234567-abcd-0123-4567-0123456789ab)
118 # FIXME This works with no more than 2 secondaries
119 re_nodelist = re.compile(node_elem + "(?:," + node_elem + ")?$")
120 re_vol = re.compile(r"^\s+logical_id:\s+(\S+)$")
121 re_drbdnode = re.compile(r"^\s+node[AB]:\s+([^\s,]+),\s+minor=([0-9]+)$")
125 for line in info_out.splitlines():
126 m = re_node.match(line)
129 m2 = re_nodelist.match(nodestr)
131 nodes.extend(filter(None, m2.groups()))
133 nodes.append(nodestr)
134 m = re_vol.match(line)
136 vols.append(m.group(1))
137 m = re_drbdnode.match(line)
140 minor = int(m.group(2))
141 if drbd_min.get(node) is not None:
142 drbd_min[node].append(minor)
144 drbd_min[node] = [minor]
147 assert len(nodes) < 2 or vols
152 "drbd-minors": drbd_min,
156 def _DestroyInstanceVolumes(instance):
157 """Remove all the LVM volumes of an instance.
159 This is used to simulate HW errors (dead nodes, broken disks...); the
160 configuration of the instance is not affected.
161 @type instance: dictionary
162 @param instance: the instance
165 info = _GetInstanceInfo(instance.name)
166 vols = info["volumes"]
167 for node in info["nodes"]:
168 AssertCommand(["lvremove", "-f"] + vols, node=node)
171 def _GetInstanceField(instance, field):
172 """Get the value of a field of an instance.
174 @type instance: string
175 @param instance: Instance name
177 @param field: Name of the field
181 master = qa_config.GetMasterNode()
182 infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
183 "--units", "m", "-o", field, instance])
184 return qa_utils.GetCommandOutput(master.primary, infocmd).strip()
187 def _GetBoolInstanceField(instance, field):
188 """Get the Boolean value of a field of an instance.
190 @type instance: string
191 @param instance: Instance name
193 @param field: Name of the field
197 info_out = _GetInstanceField(instance, field)
200 elif info_out == "N":
203 raise qa_error.Error("Field %s of instance %s has a non-Boolean value:"
204 " %s" % (field, instance, info_out))
207 def _GetNumInstanceField(instance, field):
208 """Get a numeric value of a field of an instance.
210 @type instance: string
211 @param instance: Instance name
213 @param field: Name of the field
217 info_out = _GetInstanceField(instance, field)
222 ret = float(info_out)
224 raise qa_error.Error("Field %s of instance %s has a non-numeric value:"
225 " %s" % (field, instance, info_out))
229 def GetInstanceSpec(instance, spec):
230 """Return the current spec for the given parameter.
232 @type instance: string
233 @param instance: Instance name
235 @param spec: one of the supported parameters: "mem-size", "cpu-count",
236 "disk-count", "disk-size", "nic-count"
238 @return: (minspec, maxspec); minspec and maxspec can be different only for
243 "mem-size": ["be/minmem", "be/maxmem"],
244 "cpu-count": ["vcpus"],
245 "disk-count": ["disk.count"],
246 "disk-size": ["disk.size/ "],
247 "nic-count": ["nic.count"],
249 # For disks, first we need the number of disks
250 if spec == "disk-size":
251 (numdisk, _) = GetInstanceSpec(instance, "disk-count")
252 fields = ["disk.size/%s" % k for k in range(0, numdisk)]
254 assert spec in specmap, "%s not in %s" % (spec, specmap)
255 fields = specmap[spec]
256 values = [_GetNumInstanceField(instance, f) for f in fields]
257 return (min(values), max(values))
260 def IsFailoverSupported(instance):
261 return instance.disk_template in constants.DTS_MIRRORED
264 def IsMigrationSupported(instance):
265 return instance.disk_template in constants.DTS_MIRRORED
268 def IsDiskReplacingSupported(instance):
269 return instance.disk_template == constants.DT_DRBD8
272 def TestInstanceAddWithPlainDisk(nodes, fail=False):
273 """gnt-instance add -t plain"""
274 assert len(nodes) == 1
275 instance = _DiskTest(nodes[0].primary, constants.DT_PLAIN, fail=fail)
277 qa_utils.RunInstanceCheck(instance, True)
281 @InstanceCheck(None, INST_UP, RETURN_VALUE)
282 def TestInstanceAddWithDrbdDisk(nodes):
283 """gnt-instance add -t drbd"""
284 assert len(nodes) == 2
285 return _DiskTest(":".join(map(operator.attrgetter("primary"), nodes)),
289 @InstanceCheck(None, INST_UP, RETURN_VALUE)
290 def TestInstanceAddDiskless(nodes):
291 """gnt-instance add -t diskless"""
292 assert len(nodes) == 1
293 return _DiskTest(nodes[0].primary, constants.DT_DISKLESS)
296 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
297 def TestInstanceRemove(instance):
298 """gnt-instance remove"""
299 AssertCommand(["gnt-instance", "remove", "-f", instance.name])
302 @InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
303 def TestInstanceStartup(instance):
304 """gnt-instance startup"""
305 AssertCommand(["gnt-instance", "startup", instance.name])
308 @InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
309 def TestInstanceShutdown(instance):
310 """gnt-instance shutdown"""
311 AssertCommand(["gnt-instance", "shutdown", instance.name])
314 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
315 def TestInstanceReboot(instance):
316 """gnt-instance reboot"""
317 options = qa_config.get("options", {})
318 reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
320 for rtype in reboot_types:
321 AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
323 AssertCommand(["gnt-instance", "shutdown", name])
324 qa_utils.RunInstanceCheck(instance, False)
325 AssertCommand(["gnt-instance", "reboot", name])
327 master = qa_config.GetMasterNode()
328 cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
329 result_output = qa_utils.GetCommandOutput(master.primary,
330 utils.ShellQuoteArgs(cmd))
331 AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
334 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
335 def TestInstanceReinstall(instance):
336 """gnt-instance reinstall"""
337 if instance.disk_template == constants.DT_DISKLESS:
338 print qa_utils.FormatInfo("Test not supported for diskless instances")
341 AssertCommand(["gnt-instance", "reinstall", "-f", instance.name])
343 # Test with non-existant OS definition
344 AssertCommand(["gnt-instance", "reinstall", "-f",
345 "--os-type=NonExistantOsForQa",
350 def _ReadSsconfInstanceList():
351 """Reads ssconf_instance_list from the master node.
354 master = qa_config.GetMasterNode()
356 ssconf_path = utils.PathJoin(pathutils.DATA_DIR,
357 "ssconf_%s" % constants.SS_INSTANCE_LIST)
359 cmd = ["cat", qa_utils.MakeNodePath(master, ssconf_path)]
361 return qa_utils.GetCommandOutput(master.primary,
362 utils.ShellQuoteArgs(cmd)).splitlines()
365 def _CheckSsconfInstanceList(instance):
366 """Checks if a certain instance is in the ssconf instance list.
368 @type instance: string
369 @param instance: Instance name
372 AssertIn(qa_utils.ResolveInstanceName(instance),
373 _ReadSsconfInstanceList())
376 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
377 def TestInstanceRenameAndBack(rename_source, rename_target):
378 """gnt-instance rename
380 This must leave the instance with the original name, not the target
384 _CheckSsconfInstanceList(rename_source)
386 # first do a rename to a different actual name, expecting it to fail
387 qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
389 AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
391 _CheckSsconfInstanceList(rename_source)
393 qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
395 # Check instance volume tags correctly updated
396 # FIXME: this is LVM specific!
397 info = _GetInstanceInfo(rename_source)
398 tags_cmd = ("lvs -o tags --noheadings %s | grep " %
399 (" ".join(info["volumes"]), ))
401 # and now rename instance to rename_target...
402 AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
403 _CheckSsconfInstanceList(rename_target)
404 qa_utils.RunInstanceCheck(rename_source, False)
405 qa_utils.RunInstanceCheck(rename_target, False)
407 # NOTE: tags might not be the exactly as the instance name, due to
408 # charset restrictions; hence the test might be flaky
409 if rename_source != rename_target:
410 for node in info["nodes"]:
411 AssertCommand(tags_cmd + rename_source, node=node, fail=True)
412 AssertCommand(tags_cmd + rename_target, node=node, fail=False)
415 AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
416 _CheckSsconfInstanceList(rename_source)
417 qa_utils.RunInstanceCheck(rename_target, False)
419 if rename_source != rename_target:
420 for node in info["nodes"]:
421 AssertCommand(tags_cmd + rename_source, node=node, fail=False)
422 AssertCommand(tags_cmd + rename_target, node=node, fail=True)
425 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
426 def TestInstanceFailover(instance):
427 """gnt-instance failover"""
428 if not IsFailoverSupported(instance):
429 print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
433 cmd = ["gnt-instance", "failover", "--force", instance.name]
437 qa_utils.RunInstanceCheck(instance, True)
443 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
444 def TestInstanceMigrate(instance, toggle_always_failover=True):
445 """gnt-instance migrate"""
446 if not IsMigrationSupported(instance):
447 print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
451 cmd = ["gnt-instance", "migrate", "--force", instance.name]
452 af_par = constants.BE_ALWAYS_FAILOVER
453 af_field = "be/" + constants.BE_ALWAYS_FAILOVER
454 af_init_val = _GetBoolInstanceField(instance.name, af_field)
458 # TODO: Verify the choice between failover and migration
459 qa_utils.RunInstanceCheck(instance, True)
461 # ... and back (possibly with always_failover toggled)
462 if toggle_always_failover:
463 AssertCommand(["gnt-instance", "modify", "-B",
464 ("%s=%s" % (af_par, not af_init_val)),
467 # TODO: Verify the choice between failover and migration
468 qa_utils.RunInstanceCheck(instance, True)
469 if toggle_always_failover:
470 AssertCommand(["gnt-instance", "modify", "-B",
471 ("%s=%s" % (af_par, af_init_val)), instance.name])
473 # TODO: Split into multiple tests
474 AssertCommand(["gnt-instance", "shutdown", instance.name])
475 qa_utils.RunInstanceCheck(instance, False)
476 AssertCommand(cmd, fail=True)
477 AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
479 AssertCommand(["gnt-instance", "start", instance.name])
481 # @InstanceCheck enforces the check that the instance is running
482 qa_utils.RunInstanceCheck(instance, True)
484 AssertCommand(["gnt-instance", "modify", "-B",
486 (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
490 qa_utils.RunInstanceCheck(instance, True)
491 # TODO: Verify that a failover has been done instead of a migration
493 # TODO: Verify whether the default value is restored here (not hardcoded)
494 AssertCommand(["gnt-instance", "modify", "-B",
496 (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
500 qa_utils.RunInstanceCheck(instance, True)
503 def TestInstanceInfo(instance):
504 """gnt-instance info"""
505 AssertCommand(["gnt-instance", "info", instance.name])
508 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
509 def TestInstanceModify(instance):
510 """gnt-instance modify"""
511 default_hv = qa_config.GetDefaultHypervisor()
513 # Assume /sbin/init exists on all systems
514 test_kernel = "/sbin/init"
515 test_initrd = test_kernel
517 orig_maxmem = qa_config.get(constants.BE_MAXMEM)
518 orig_minmem = qa_config.get(constants.BE_MINMEM)
519 #orig_bridge = qa_config.get("bridge", "xen-br0")
522 ["-B", "%s=128" % constants.BE_MINMEM],
523 ["-B", "%s=128" % constants.BE_MAXMEM],
524 ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
525 constants.BE_MAXMEM, orig_maxmem)],
526 ["-B", "%s=2" % constants.BE_VCPUS],
527 ["-B", "%s=1" % constants.BE_VCPUS],
528 ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
529 ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
530 ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
532 ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
533 ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
536 #["--bridge", "xen-br1"],
537 #["--bridge", orig_bridge],
540 if default_hv == constants.HT_XEN_PVM:
542 ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
543 ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
544 ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
546 elif default_hv == constants.HT_XEN_HVM:
548 ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
549 ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
553 AssertCommand(["gnt-instance", "modify"] + alist + [instance.name])
556 AssertCommand(["gnt-instance", "modify", instance.name], fail=True)
558 # Marking offline while instance is running must fail...
559 AssertCommand(["gnt-instance", "modify", "--offline", instance.name],
562 # ...while making it online is ok, and should work
563 AssertCommand(["gnt-instance", "modify", "--online", instance.name])
566 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
567 def TestInstanceStoppedModify(instance):
568 """gnt-instance modify (stopped instance)"""
571 # Instance was not marked offline; try marking it online once more
572 AssertCommand(["gnt-instance", "modify", "--online", name])
574 # Mark instance as offline
575 AssertCommand(["gnt-instance", "modify", "--offline", name])
577 # When the instance is offline shutdown should only work with --force,
578 # while start should never work
579 AssertCommand(["gnt-instance", "shutdown", name], fail=True)
580 AssertCommand(["gnt-instance", "shutdown", "--force", name])
581 AssertCommand(["gnt-instance", "start", name], fail=True)
582 AssertCommand(["gnt-instance", "start", "--force", name], fail=True)
584 # Also do offline to offline
585 AssertCommand(["gnt-instance", "modify", "--offline", name])
588 AssertCommand(["gnt-instance", "modify", "--online", name])
591 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
592 def TestInstanceConvertDiskToPlain(instance, inodes):
593 """gnt-instance modify -t"""
596 template = instance.disk_template
597 if template != constants.DT_DRBD8:
598 print qa_utils.FormatInfo("Unsupported template %s, skipping conversion"
602 assert len(inodes) == 2
603 AssertCommand(["gnt-instance", "modify", "-t", constants.DT_PLAIN, name])
604 AssertCommand(["gnt-instance", "modify", "-t", constants.DT_DRBD8,
605 "-n", inodes[1].primary, name])
608 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
609 def TestInstanceGrowDisk(instance):
610 """gnt-instance grow-disk"""
611 if qa_config.GetExclusiveStorage():
612 print qa_utils.FormatInfo("Test not supported with exclusive_storage")
615 if instance.disk_template == constants.DT_DISKLESS:
616 print qa_utils.FormatInfo("Test not supported for diskless instances")
620 all_size = qa_config.get("disk")
621 all_grow = qa_config.get("disk-growth")
624 # missing disk sizes but instance grow disk has been enabled,
625 # let's set fixed/nomimal growth
626 all_grow = ["128M" for _ in all_size]
628 for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
629 # succeed in grow by amount
630 AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
631 # fail in grow to the old size
632 AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
634 # succeed to grow to old size + 2 * growth
635 int_size = utils.ParseUnit(size)
636 int_grow = utils.ParseUnit(grow)
637 AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
638 str(int_size + 2 * int_grow)])
641 def TestInstanceList():
642 """gnt-instance list"""
643 qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
646 def TestInstanceListFields():
647 """gnt-instance list-fields"""
648 qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
651 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
652 def TestInstanceConsole(instance):
653 """gnt-instance console"""
654 AssertCommand(["gnt-instance", "console", "--show-cmd", instance.name])
657 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
658 def TestReplaceDisks(instance, curr_nodes, other_nodes):
659 """gnt-instance replace-disks"""
661 cmd = ["gnt-instance", "replace-disks"]
663 cmd.append(instance.name)
666 if not IsDiskReplacingSupported(instance):
667 print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
671 # Currently all supported templates have one primary and one secondary node
672 assert len(curr_nodes) == 2
673 snode = curr_nodes[1]
674 assert len(other_nodes) == 1
675 othernode = other_nodes[0]
677 options = qa_config.get("options", {})
678 use_ialloc = options.get("use-iallocators", True)
682 # A placeholder; the actual command choice depends on use_ialloc
684 # Restore the original secondary
685 ["--new-secondary=%s" % snode.primary],
689 data = ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT]
691 data = ["--new-secondary=%s" % othernode.primary]
692 AssertCommand(buildcmd(data))
694 AssertCommand(buildcmd(["-a"]))
695 AssertCommand(["gnt-instance", "stop", instance.name])
696 AssertCommand(buildcmd(["-a"]), fail=True)
697 AssertCommand(["gnt-instance", "activate-disks", instance.name])
698 AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
700 AssertCommand(buildcmd(["-a"]))
701 AssertCommand(["gnt-instance", "start", instance.name])
704 def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
706 """Execute gnt-instance recreate-disks and check the result
708 @param cmdargs: Arguments (instance name excluded)
709 @param instance: Instance to operate on
710 @param fail: True if the command is expected to fail
711 @param check: If True and fail is False, check that the disks work
712 @prama destroy: If True, destroy the old disks first
716 _DestroyInstanceVolumes(instance)
717 AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
718 [instance.name]), fail)
719 if not fail and check:
720 # Quick check that the disks are there
721 AssertCommand(["gnt-instance", "activate-disks", instance.name])
722 AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
724 AssertCommand(["gnt-instance", "deactivate-disks", instance.name])
727 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
728 def TestRecreateDisks(instance, inodes, othernodes):
729 """gnt-instance recreate-disks
731 @param instance: Instance to work on
732 @param inodes: List of the current nodes of the instance
733 @param othernodes: list/tuple of nodes where to temporarily recreate disks
736 options = qa_config.get("options", {})
737 use_ialloc = options.get("use-iallocators", True)
738 other_seq = ":".join([n.primary for n in othernodes])
739 orig_seq = ":".join([n.primary for n in inodes])
740 # These fail because the instance is running
741 _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
743 _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
745 _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
746 AssertCommand(["gnt-instance", "stop", instance.name])
747 # Disks exist: this should fail
748 _AssertRecreateDisks([], instance, fail=True, destroy=False)
749 # Recreate disks in place
750 _AssertRecreateDisks([], instance)
753 _AssertRecreateDisks(["-I", "hail"], instance)
754 # Move disks somewhere else
755 _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
758 _AssertRecreateDisks(["-n", other_seq], instance)
760 _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
761 # This and InstanceCheck decoration check that the disks are working
762 AssertCommand(["gnt-instance", "reinstall", "-f", instance.name])
763 AssertCommand(["gnt-instance", "start", instance.name])
766 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
767 def TestInstanceExport(instance, node):
768 """gnt-backup export -n ..."""
770 AssertCommand(["gnt-backup", "export", "-n", node.primary, name])
771 return qa_utils.ResolveInstanceName(name)
774 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
775 def TestInstanceExportWithRemove(instance, node):
776 """gnt-backup export --remove-instance"""
777 AssertCommand(["gnt-backup", "export", "-n", node.primary,
778 "--remove-instance", instance.name])
781 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
782 def TestInstanceExportNoTarget(instance):
783 """gnt-backup export (without target node, should fail)"""
784 AssertCommand(["gnt-backup", "export", instance.name], fail=True)
787 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
788 def TestInstanceImport(newinst, node, expnode, name):
789 """gnt-backup import"""
790 templ = constants.DT_PLAIN
791 cmd = (["gnt-backup", "import",
792 "--disk-template=%s" % templ,
794 "--src-node=%s" % expnode.primary,
795 "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
796 "--node=%s" % node.primary] +
797 _GetGenericAddParameters(newinst, templ,
798 force_mac=constants.VALUE_GENERATE))
799 cmd.append(newinst.name)
801 newinst.SetDiskTemplate(templ)
804 def TestBackupList(expnode):
805 """gnt-backup list"""
806 AssertCommand(["gnt-backup", "list", "--node=%s" % expnode.primary])
808 qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
809 namefield=None, test_unknown=False)
812 def TestBackupListFields():
813 """gnt-backup list-fields"""
814 qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
817 def TestRemoveInstanceOfflineNode(instance, snode, set_offline, set_online):
818 """gtn-instance remove with an off-line node
820 @param instance: instance
821 @param snode: secondary node, to be set offline
822 @param set_offline: function to call to set the node off-line
823 @param set_online: function to call to set the node on-line
826 info = _GetInstanceInfo(instance.name)
829 TestInstanceRemove(instance)
832 # Clean up the disks on the offline node
833 for minor in info["drbd-minors"][snode.primary]:
834 AssertCommand(["drbdsetup", str(minor), "down"], node=snode)
835 AssertCommand(["lvremove", "-f"] + info["volumes"], node=snode)