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, 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)))
52 for idx, size in enumerate(qa_config.get("disk")):
53 params.extend(["--disk", "%s:size=%s" % (idx, size)])
55 # Set static MAC address if configured
59 nic0_mac = qa_config.GetInstanceNicMac(inst)
61 params.extend(["--net", "0:mac=%s" % nic0_mac])
66 def _DiskTest(node, disk_template, fail=False):
67 instance = qa_config.AcquireInstance()
69 cmd = (["gnt-instance", "add",
70 "--os-type=%s" % qa_config.get("os"),
71 "--disk-template=%s" % disk_template,
73 _GetGenericAddParameters(instance))
74 cmd.append(instance["name"])
76 AssertCommand(cmd, fail=fail)
79 _CheckSsconfInstanceList(instance["name"])
80 qa_config.SetInstanceTemplate(instance, disk_template)
84 qa_config.ReleaseInstance(instance)
87 # Handle the case where creation is expected to fail
89 qa_config.ReleaseInstance(instance)
93 def _GetInstanceInfo(instance):
94 """Return information about the actual state of an instance.
96 @type instance: string
97 @param instance: the instance name
98 @return: a dictionary with the following keys:
99 - "nodes": instance nodes, a list of strings
100 - "volumes": instance volume IDs, a list of strings
101 - "drbd-minors": DRBD minors used by the instance, a dictionary where
102 keys are nodes, and values are lists of integers (or an empty
103 dictionary for non-DRBD instances)
106 master = qa_config.GetMasterNode()
107 infocmd = utils.ShellQuoteArgs(["gnt-instance", "info", instance])
108 info_out = qa_utils.GetCommandOutput(master["primary"], infocmd)
109 re_node = re.compile(r"^\s+-\s+(?:primary|secondaries):\s+(\S.+)$")
110 node_elem = r"([^,()]+)(?:\s+\([^)]+\))?"
111 # re_nodelist matches a list of nodes returned by gnt-instance info, e.g.:
113 # node2.fqdn,node3.fqdn
114 # node4.fqdn (group mygroup, group UUID 01234567-abcd-0123-4567-0123456789ab)
115 # FIXME This works with no more than 2 secondaries
116 re_nodelist = re.compile(node_elem + "(?:," + node_elem + ")?$")
117 re_vol = re.compile(r"^\s+logical_id:\s+(\S+)$")
118 re_drbdnode = re.compile(r"^\s+node[AB]:\s+([^\s,]+),\s+minor=([0-9]+)$")
122 for line in info_out.splitlines():
123 m = re_node.match(line)
126 m2 = re_nodelist.match(nodestr)
128 nodes.extend(filter(None, m2.groups()))
130 nodes.append(nodestr)
131 m = re_vol.match(line)
133 vols.append(m.group(1))
134 m = re_drbdnode.match(line)
137 minor = int(m.group(2))
138 if drbd_min.get(node) is not None:
139 drbd_min[node].append(minor)
141 drbd_min[node] = [minor]
144 return {"nodes": nodes, "volumes": vols, "drbd-minors": drbd_min}
147 def _DestroyInstanceVolumes(instance):
148 """Remove all the LVM volumes of an instance.
150 This is used to simulate HW errors (dead nodes, broken disks...); the
151 configuration of the instance is not affected.
152 @type instance: dictionary
153 @param instance: the instance
156 info = _GetInstanceInfo(instance["name"])
157 vols = info["volumes"]
158 for node in info["nodes"]:
159 AssertCommand(["lvremove", "-f"] + vols, node=node)
162 def _GetInstanceField(instance, field):
163 """Get the value of a field of an instance.
165 @type instance: string
166 @param instance: Instance name
168 @param field: Name of the field
172 master = qa_config.GetMasterNode()
173 infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
174 "--units", "m", "-o", field, instance])
175 return qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
178 def _GetBoolInstanceField(instance, field):
179 """Get the Boolean value of a field of an instance.
181 @type instance: string
182 @param instance: Instance name
184 @param field: Name of the field
188 info_out = _GetInstanceField(instance, field)
191 elif info_out == "N":
194 raise qa_error.Error("Field %s of instance %s has a non-Boolean value:"
195 " %s" % (field, instance, info_out))
198 def _GetNumInstanceField(instance, field):
199 """Get a numeric value of a field of an instance.
201 @type instance: string
202 @param instance: Instance name
204 @param field: Name of the field
208 info_out = _GetInstanceField(instance, field)
213 ret = float(info_out)
215 raise qa_error.Error("Field %s of instance %s has a non-numeric value:"
216 " %s" % (field, instance, info_out))
220 def GetInstanceSpec(instance, spec):
221 """Return the current spec for the given parameter.
223 @type instance: string
224 @param instance: Instance name
226 @param spec: one of the supported parameters: "mem-size", "cpu-count",
227 "disk-count", "disk-size", "nic-count"
229 @return: (minspec, maxspec); minspec and maxspec can be different only for
234 "mem-size": ["be/minmem", "be/maxmem"],
235 "cpu-count": ["vcpus"],
236 "disk-count": ["disk.count"],
237 "disk-size": ["disk.size/ "],
238 "nic-count": ["nic.count"],
240 # For disks, first we need the number of disks
241 if spec == "disk-size":
242 (numdisk, _) = GetInstanceSpec(instance, "disk-count")
243 fields = ["disk.size/%s" % k for k in range(0, numdisk)]
245 assert spec in specmap, "%s not in %s" % (spec, specmap)
246 fields = specmap[spec]
247 values = [_GetNumInstanceField(instance, f) for f in fields]
248 return (min(values), max(values))
251 def IsFailoverSupported(instance):
252 templ = qa_config.GetInstanceTemplate(instance)
253 return templ in constants.DTS_MIRRORED
256 def IsMigrationSupported(instance):
257 templ = qa_config.GetInstanceTemplate(instance)
258 return templ in constants.DTS_MIRRORED
261 def IsDiskReplacingSupported(instance):
262 templ = qa_config.GetInstanceTemplate(instance)
263 return templ == constants.DT_DRBD8
266 def TestInstanceAddWithPlainDisk(nodes, fail=False):
267 """gnt-instance add -t plain"""
268 assert len(nodes) == 1
269 instance = _DiskTest(nodes[0]["primary"], constants.DT_PLAIN, fail=fail)
271 qa_utils.RunInstanceCheck(instance, True)
275 @InstanceCheck(None, INST_UP, RETURN_VALUE)
276 def TestInstanceAddWithDrbdDisk(nodes):
277 """gnt-instance add -t drbd"""
278 assert len(nodes) == 2
279 return _DiskTest(":".join(map(operator.itemgetter("primary"), nodes)),
283 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
284 def TestInstanceRemove(instance):
285 """gnt-instance remove"""
286 AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
288 qa_config.ReleaseInstance(instance)
291 @InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
292 def TestInstanceStartup(instance):
293 """gnt-instance startup"""
294 AssertCommand(["gnt-instance", "startup", instance["name"]])
297 @InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
298 def TestInstanceShutdown(instance):
299 """gnt-instance shutdown"""
300 AssertCommand(["gnt-instance", "shutdown", instance["name"]])
303 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
304 def TestInstanceReboot(instance):
305 """gnt-instance reboot"""
306 options = qa_config.get("options", {})
307 reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
308 name = instance["name"]
309 for rtype in reboot_types:
310 AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
312 AssertCommand(["gnt-instance", "shutdown", name])
313 qa_utils.RunInstanceCheck(instance, False)
314 AssertCommand(["gnt-instance", "reboot", name])
316 master = qa_config.GetMasterNode()
317 cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
318 result_output = qa_utils.GetCommandOutput(master["primary"],
319 utils.ShellQuoteArgs(cmd))
320 AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
323 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
324 def TestInstanceReinstall(instance):
325 """gnt-instance reinstall"""
326 AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
328 # Test with non-existant OS definition
329 AssertCommand(["gnt-instance", "reinstall", "-f",
330 "--os-type=NonExistantOsForQa",
335 def _ReadSsconfInstanceList():
336 """Reads ssconf_instance_list from the master node.
339 master = qa_config.GetMasterNode()
341 cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
342 "ssconf_%s" % constants.SS_INSTANCE_LIST)]
344 return qa_utils.GetCommandOutput(master["primary"],
345 utils.ShellQuoteArgs(cmd)).splitlines()
348 def _CheckSsconfInstanceList(instance):
349 """Checks if a certain instance is in the ssconf instance list.
351 @type instance: string
352 @param instance: Instance name
355 AssertIn(qa_utils.ResolveInstanceName(instance),
356 _ReadSsconfInstanceList())
359 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
360 def TestInstanceRenameAndBack(rename_source, rename_target):
361 """gnt-instance rename
363 This must leave the instance with the original name, not the target
367 _CheckSsconfInstanceList(rename_source)
369 # first do a rename to a different actual name, expecting it to fail
370 qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
372 AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
374 _CheckSsconfInstanceList(rename_source)
376 qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
378 # Check instance volume tags correctly updated
379 # FIXME: this is LVM specific!
380 info = _GetInstanceInfo(rename_source)
381 tags_cmd = ("lvs -o tags --noheadings %s | grep " %
382 (" ".join(info["volumes"]), ))
384 # and now rename instance to rename_target...
385 AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
386 _CheckSsconfInstanceList(rename_target)
387 qa_utils.RunInstanceCheck(rename_source, False)
388 qa_utils.RunInstanceCheck(rename_target, False)
390 # NOTE: tags might not be the exactly as the instance name, due to
391 # charset restrictions; hence the test might be flaky
392 if rename_source != rename_target:
393 for node in info["nodes"]:
394 AssertCommand(tags_cmd + rename_source, node=node, fail=True)
395 AssertCommand(tags_cmd + rename_target, node=node, fail=False)
398 AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
399 _CheckSsconfInstanceList(rename_source)
400 qa_utils.RunInstanceCheck(rename_target, False)
402 if rename_source != rename_target:
403 for node in info["nodes"]:
404 AssertCommand(tags_cmd + rename_source, node=node, fail=False)
405 AssertCommand(tags_cmd + rename_target, node=node, fail=True)
408 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
409 def TestInstanceFailover(instance):
410 """gnt-instance failover"""
411 if not IsFailoverSupported(instance):
412 print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
416 cmd = ["gnt-instance", "failover", "--force", instance["name"]]
420 qa_utils.RunInstanceCheck(instance, True)
426 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
427 def TestInstanceMigrate(instance, toggle_always_failover=True):
428 """gnt-instance migrate"""
429 if not IsMigrationSupported(instance):
430 print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
434 cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
435 af_par = constants.BE_ALWAYS_FAILOVER
436 af_field = "be/" + constants.BE_ALWAYS_FAILOVER
437 af_init_val = _GetBoolInstanceField(instance["name"], af_field)
441 # TODO: Verify the choice between failover and migration
442 qa_utils.RunInstanceCheck(instance, True)
444 # ... and back (possibly with always_failover toggled)
445 if toggle_always_failover:
446 AssertCommand(["gnt-instance", "modify", "-B",
447 ("%s=%s" % (af_par, not af_init_val)),
450 # TODO: Verify the choice between failover and migration
451 qa_utils.RunInstanceCheck(instance, True)
452 if toggle_always_failover:
453 AssertCommand(["gnt-instance", "modify", "-B",
454 ("%s=%s" % (af_par, af_init_val)), instance["name"]])
456 # TODO: Split into multiple tests
457 AssertCommand(["gnt-instance", "shutdown", instance["name"]])
458 qa_utils.RunInstanceCheck(instance, False)
459 AssertCommand(cmd, fail=True)
460 AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
462 AssertCommand(["gnt-instance", "start", instance["name"]])
464 # @InstanceCheck enforces the check that the instance is running
465 qa_utils.RunInstanceCheck(instance, True)
467 AssertCommand(["gnt-instance", "modify", "-B",
469 (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
473 qa_utils.RunInstanceCheck(instance, True)
474 # TODO: Verify that a failover has been done instead of a migration
476 # TODO: Verify whether the default value is restored here (not hardcoded)
477 AssertCommand(["gnt-instance", "modify", "-B",
479 (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
483 qa_utils.RunInstanceCheck(instance, True)
486 def TestInstanceInfo(instance):
487 """gnt-instance info"""
488 AssertCommand(["gnt-instance", "info", instance["name"]])
491 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
492 def TestInstanceModify(instance):
493 """gnt-instance modify"""
494 default_hv = qa_config.GetDefaultHypervisor()
496 # Assume /sbin/init exists on all systems
497 test_kernel = "/sbin/init"
498 test_initrd = test_kernel
500 orig_maxmem = qa_config.get(constants.BE_MAXMEM)
501 orig_minmem = qa_config.get(constants.BE_MINMEM)
502 #orig_bridge = qa_config.get("bridge", "xen-br0")
505 ["-B", "%s=128" % constants.BE_MINMEM],
506 ["-B", "%s=128" % constants.BE_MAXMEM],
507 ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
508 constants.BE_MAXMEM, orig_maxmem)],
509 ["-B", "%s=2" % constants.BE_VCPUS],
510 ["-B", "%s=1" % constants.BE_VCPUS],
511 ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
512 ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
513 ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
515 ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
516 ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
519 #["--bridge", "xen-br1"],
520 #["--bridge", orig_bridge],
523 if default_hv == constants.HT_XEN_PVM:
525 ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
526 ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
527 ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
529 elif default_hv == constants.HT_XEN_HVM:
531 ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
532 ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
536 AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
539 AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
541 # Marking offline while instance is running must fail...
542 AssertCommand(["gnt-instance", "modify", "--offline", instance["name"]],
545 # ...while making it online is ok, and should work
546 AssertCommand(["gnt-instance", "modify", "--online", instance["name"]])
549 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
550 def TestInstanceStoppedModify(instance):
551 """gnt-instance modify (stopped instance)"""
552 name = instance["name"]
554 # Instance was not marked offline; try marking it online once more
555 AssertCommand(["gnt-instance", "modify", "--online", name])
557 # Mark instance as offline
558 AssertCommand(["gnt-instance", "modify", "--offline", name])
560 # When the instance is offline shutdown should only work with --force,
561 # while start should never work
562 AssertCommand(["gnt-instance", "shutdown", name], fail=True)
563 AssertCommand(["gnt-instance", "shutdown", "--force", name])
564 AssertCommand(["gnt-instance", "start", name], fail=True)
565 AssertCommand(["gnt-instance", "start", "--force", name], fail=True)
567 # Also do offline to offline
568 AssertCommand(["gnt-instance", "modify", "--offline", name])
571 AssertCommand(["gnt-instance", "modify", "--online", name])
574 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
575 def TestInstanceConvertDiskToPlain(instance, inodes):
576 """gnt-instance modify -t"""
577 name = instance["name"]
578 template = qa_config.GetInstanceTemplate(instance)
579 if template != "drbd":
580 print qa_utils.FormatInfo("Unsupported template %s, skipping conversion"
583 assert len(inodes) == 2
584 AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
585 AssertCommand(["gnt-instance", "modify", "-t", "drbd",
586 "-n", inodes[1]["primary"], name])
589 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
590 def TestInstanceGrowDisk(instance):
591 """gnt-instance grow-disk"""
592 if qa_config.GetExclusiveStorage():
593 print qa_utils.FormatInfo("Test not supported with exclusive_storage")
595 name = instance["name"]
596 all_size = qa_config.get("disk")
597 all_grow = qa_config.get("disk-growth")
599 # missing disk sizes but instance grow disk has been enabled,
600 # let's set fixed/nomimal growth
601 all_grow = ["128M" for _ in all_size]
602 for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
603 # succeed in grow by amount
604 AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
605 # fail in grow to the old size
606 AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
608 # succeed to grow to old size + 2 * growth
609 int_size = utils.ParseUnit(size)
610 int_grow = utils.ParseUnit(grow)
611 AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
612 str(int_size + 2 * int_grow)])
615 def TestInstanceList():
616 """gnt-instance list"""
617 qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
620 def TestInstanceListFields():
621 """gnt-instance list-fields"""
622 qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
625 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
626 def TestInstanceConsole(instance):
627 """gnt-instance console"""
628 AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
631 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
632 def TestReplaceDisks(instance, curr_nodes, other_nodes):
633 """gnt-instance replace-disks"""
635 cmd = ["gnt-instance", "replace-disks"]
637 cmd.append(instance["name"])
640 if not IsDiskReplacingSupported(instance):
641 print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
645 # Currently all supported templates have one primary and one secondary node
646 assert len(curr_nodes) == 2
647 snode = curr_nodes[1]
648 assert len(other_nodes) == 1
649 othernode = other_nodes[0]
651 options = qa_config.get("options", {})
652 use_ialloc = options.get("use-iallocators", True)
656 # A placeholder; the actual command choice depends on use_ialloc
658 # Restore the original secondary
659 ["--new-secondary=%s" % snode["primary"]],
663 data = ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT]
665 data = ["--new-secondary=%s" % othernode["primary"]]
666 AssertCommand(buildcmd(data))
668 AssertCommand(buildcmd(["-a"]))
669 AssertCommand(["gnt-instance", "stop", instance["name"]])
670 AssertCommand(buildcmd(["-a"]), fail=True)
671 AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
672 AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
674 AssertCommand(buildcmd(["-a"]))
675 AssertCommand(["gnt-instance", "start", instance["name"]])
678 def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
680 """Execute gnt-instance recreate-disks and check the result
682 @param cmdargs: Arguments (instance name excluded)
683 @param instance: Instance to operate on
684 @param fail: True if the command is expected to fail
685 @param check: If True and fail is False, check that the disks work
686 @prama destroy: If True, destroy the old disks first
690 _DestroyInstanceVolumes(instance)
691 AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
692 [instance["name"]]), fail)
693 if not fail and check:
694 # Quick check that the disks are there
695 AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
696 AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
698 AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
701 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
702 def TestRecreateDisks(instance, inodes, othernodes):
703 """gnt-instance recreate-disks
705 @param instance: Instance to work on
706 @param inodes: List of the current nodes of the instance
707 @param othernodes: list/tuple of nodes where to temporarily recreate disks
710 options = qa_config.get("options", {})
711 use_ialloc = options.get("use-iallocators", True)
712 other_seq = ":".join([n["primary"] for n in othernodes])
713 orig_seq = ":".join([n["primary"] for n in inodes])
714 # These fail because the instance is running
715 _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
717 _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
719 _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
720 AssertCommand(["gnt-instance", "stop", instance["name"]])
721 # Disks exist: this should fail
722 _AssertRecreateDisks([], instance, fail=True, destroy=False)
723 # Recreate disks in place
724 _AssertRecreateDisks([], instance)
727 _AssertRecreateDisks(["-I", "hail"], instance)
728 # Move disks somewhere else
729 _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
732 _AssertRecreateDisks(["-n", other_seq], instance)
734 _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
735 # This and InstanceCheck decoration check that the disks are working
736 AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
737 AssertCommand(["gnt-instance", "start", instance["name"]])
740 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
741 def TestInstanceExport(instance, node):
742 """gnt-backup export -n ..."""
743 name = instance["name"]
744 AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
745 return qa_utils.ResolveInstanceName(name)
748 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
749 def TestInstanceExportWithRemove(instance, node):
750 """gnt-backup export --remove-instance"""
751 AssertCommand(["gnt-backup", "export", "-n", node["primary"],
752 "--remove-instance", instance["name"]])
753 qa_config.ReleaseInstance(instance)
756 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
757 def TestInstanceExportNoTarget(instance):
758 """gnt-backup export (without target node, should fail)"""
759 AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
762 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
763 def TestInstanceImport(newinst, node, expnode, name):
764 """gnt-backup import"""
765 templ = constants.DT_PLAIN
766 cmd = (["gnt-backup", "import",
767 "--disk-template=%s" % templ,
769 "--src-node=%s" % expnode["primary"],
770 "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
771 "--node=%s" % node["primary"]] +
772 _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
773 cmd.append(newinst["name"])
775 qa_config.SetInstanceTemplate(newinst, templ)
778 def TestBackupList(expnode):
779 """gnt-backup list"""
780 AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
782 qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
783 namefield=None, test_unknown=False)
786 def TestBackupListFields():
787 """gnt-backup list-fields"""
788 qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
791 def TestRemoveInstanceOfflineNode(instance, snode, set_offline, set_online):
792 """gtn-instance remove with an off-line node
794 @param instance: instance
795 @param snode: secondary node, to be set offline
796 @param set_offline: function to call to set the node off-line
797 @param set_online: function to call to set the node on-line
800 info = _GetInstanceInfo(instance["name"])
803 TestInstanceRemove(instance)
806 # Clean up the disks on the offline node
807 for minor in info["drbd-minors"][snode["primary"]]:
808 AssertCommand(["drbdsetup", str(minor), "down"], node=snode)
809 AssertCommand(["lvremove", "-f"] + info["volumes"], node=snode)