from ganeti import objects
from ganeti import compat
from ganeti import rpc
+from ganeti import pathutils
+from ganeti.masterd import iallocator
from ganeti.hypervisor import hv_xen
import testutils
c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
# Neither node nor iallocator given
- op.iallocator = None
- op.node = None
- c_i()
- self.assertEqual(lu.op.iallocator, default_iallocator)
- self.assertEqual(lu.op.node, None)
+ for n in (None, []):
+ op.iallocator = None
+ op.node = n
+ c_i()
+ self.assertEqual(lu.op.iallocator, default_iallocator)
+ self.assertEqual(lu.op.node, n)
# Both, iallocator and node given
- op.iallocator = "test"
- op.node = "test"
- self.assertRaises(errors.OpPrereqError, c_i)
+ for a in ("test", constants.DEFAULT_IALLOCATOR_SHORTCUT):
+ op.iallocator = a
+ op.node = "test"
+ self.assertRaises(errors.OpPrereqError, c_i)
# Only iallocator given
- op.iallocator = other_iallocator
- op.node = None
- c_i()
- self.assertEqual(lu.op.iallocator, other_iallocator)
- self.assertEqual(lu.op.node, None)
+ for n in (None, []):
+ op.iallocator = other_iallocator
+ op.node = n
+ c_i()
+ self.assertEqual(lu.op.iallocator, other_iallocator)
+ self.assertEqual(lu.op.node, n)
# Only node given
op.iallocator = None
self.assertEqual(lu.op.iallocator, None)
self.assertEqual(lu.op.node, "node")
+ # Asked for default iallocator, no node given
+ op.iallocator = constants.DEFAULT_IALLOCATOR_SHORTCUT
+ op.node = None
+ c_i()
+ self.assertEqual(lu.op.iallocator, default_iallocator)
+ self.assertEqual(lu.op.node, None)
+
# No node, iallocator or default iallocator
op.iallocator = None
op.node = None
cluster = objects.Cluster(modify_etc_hosts=True,
enabled_hypervisors=[constants.HT_XEN_HVM])
files_all = set([
- constants.CLUSTER_DOMAIN_SECRET_FILE,
- constants.RAPI_CERT_FILE,
- constants.RAPI_USERS_FILE,
+ pathutils.CLUSTER_DOMAIN_SECRET_FILE,
+ pathutils.RAPI_CERT_FILE,
+ pathutils.RAPI_USERS_FILE,
])
files_opt = set([
- constants.RAPI_USERS_FILE,
+ pathutils.RAPI_USERS_FILE,
hv_xen.XL_CONFIG_FILE,
- constants.VNC_PASSWORD_FILE,
+ pathutils.VNC_PASSWORD_FILE,
])
files_mc = set([
- constants.CLUSTER_CONF_FILE,
+ pathutils.CLUSTER_CONF_FILE,
])
files_vm = set([
hv_xen.XEND_CONFIG_FILE,
hv_xen.XL_CONFIG_FILE,
- constants.VNC_PASSWORD_FILE,
+ pathutils.VNC_PASSWORD_FILE,
])
nvinfo = {
master_name: rpc.RpcResult(data=(True, {
constants.NV_FILELIST: {
- constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
- constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
- constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
+ pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
+ pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
+ pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
}})),
"node2.example.com": rpc.RpcResult(data=(True, {
constants.NV_FILELIST: {
- constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
+ pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
}
})),
"node3.example.com": rpc.RpcResult(data=(True, {
constants.NV_FILELIST: {
- constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
- constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
+ pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
+ pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
}
})),
"node4.example.com": rpc.RpcResult(data=(True, {
constants.NV_FILELIST: {
- constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
- constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
- constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
- constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
+ pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
+ pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
+ pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
+ pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
}
})),
self.assertEqual(sorted(errors), sorted([
(None, ("File %s found with 2 different checksums (variant 1 on"
" node2.example.com, node3.example.com, node4.example.com;"
- " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
+ " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
(None, ("File %s is missing from node(s) node2.example.com" %
- constants.CLUSTER_DOMAIN_SECRET_FILE)),
+ pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
(None, ("File %s should not exist on node(s) node4.example.com" %
- constants.CLUSTER_CONF_FILE)),
+ pathutils.CLUSTER_CONF_FILE)),
(None, ("File %s is missing from node(s) node4.example.com" %
hv_xen.XEND_CONFIG_FILE)),
(None, ("File %s is missing from node(s) node3.example.com" %
- constants.CLUSTER_CONF_FILE)),
+ pathutils.CLUSTER_CONF_FILE)),
(None, ("File %s found with 2 different checksums (variant 1 on"
" master.example.com; variant 2 on node4.example.com)" %
- constants.CLUSTER_CONF_FILE)),
+ pathutils.CLUSTER_CONF_FILE)),
(None, ("File %s is optional, but it must exist on all or no nodes (not"
" found on master.example.com, node2.example.com,"
- " node3.example.com)" % constants.RAPI_USERS_FILE)),
+ " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
(None, ("File %s is optional, but it must exist on all or no nodes (not"
" found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
("nodata.example.com", "Node did not return file checksum data"),
class _FakeLU:
- def __init__(self, cfg=NotImplemented, proc=NotImplemented):
+ def __init__(self, cfg=NotImplemented, proc=NotImplemented,
+ rpc=NotImplemented):
self.warning_log = []
self.info_log = []
self.cfg = cfg
self.proc = proc
+ self.rpc = rpc
def LogWarning(self, text, *args):
self.warning_log.append((text, args))
]
alloc_result = (moved, [], jobs)
- assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
+ assert iallocator._NEVAC_RESULT(alloc_result)
lu = _FakeLU()
result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
alloc_result = ([], [
("inst5191.example.com", "errormsg21178"),
], [])
- assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
+ assert iallocator._NEVAC_RESULT(alloc_result)
lu = _FakeLU()
self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
])
+class _ConfigForDiskWipe:
+ def __init__(self, exp_node):
+ self._exp_node = exp_node
+
+ def SetDiskID(self, device, node):
+ assert isinstance(device, objects.Disk)
+ assert node == self._exp_node
+
+
+class _RpcForDiskWipe:
+ def __init__(self, exp_node, pause_cb, wipe_cb):
+ self._exp_node = exp_node
+ self._pause_cb = pause_cb
+ self._wipe_cb = wipe_cb
+
+ def call_blockdev_pause_resume_sync(self, node, disks, pause):
+ assert node == self._exp_node
+ return rpc.RpcResult(data=self._pause_cb(disks, pause))
+
+ def call_blockdev_wipe(self, node, bdev, offset, size):
+ assert node == self._exp_node
+ return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
+
+
+class _DiskPauseTracker:
+ def __init__(self):
+ self.history = []
+
+ def __call__(self, (disks, instance), pause):
+ assert not (set(disks) - set(instance.disks))
+
+ self.history.extend((i.logical_id, i.size, pause)
+ for i in disks)
+
+ return (True, [True] * len(disks))
+
+
+class _DiskWipeProgressTracker:
+ def __init__(self, start_offset):
+ self._start_offset = start_offset
+ self.progress = {}
+
+ def __call__(self, (disk, _), offset, size):
+ assert isinstance(offset, (long, int))
+ assert isinstance(size, (long, int))
+
+ max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
+
+ assert offset >= self._start_offset
+ assert (offset + size) <= disk.size
+
+ assert size > 0
+ assert size <= constants.MAX_WIPE_CHUNK
+ assert size <= max_chunk_size
+
+ assert offset == self._start_offset or disk.logical_id in self.progress
+
+ # Keep track of progress
+ cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
+
+ assert cur_progress == offset
+
+ # Record progress
+ self.progress[disk.logical_id] += size
+
+ return (True, None)
+
+
+class TestWipeDisks(unittest.TestCase):
+ def _FailingPauseCb(self, (disks, _), pause):
+ self.assertEqual(len(disks), 3)
+ self.assertTrue(pause)
+ # Simulate an RPC error
+ return (False, "error")
+
+ def testPauseFailure(self):
+ node_name = "node1372.example.com"
+
+ lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
+ NotImplemented),
+ cfg=_ConfigForDiskWipe(node_name))
+
+ disks = [
+ objects.Disk(dev_type=constants.LD_LV),
+ objects.Disk(dev_type=constants.LD_LV),
+ objects.Disk(dev_type=constants.LD_LV),
+ ]
+
+ instance = objects.Instance(name="inst21201",
+ primary_node=node_name,
+ disk_template=constants.DT_PLAIN,
+ disks=disks)
+
+ self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
+
+ def _FailingWipeCb(self, (disk, _), offset, size):
+ # This should only ever be called for the first disk
+ self.assertEqual(disk.logical_id, "disk0")
+ return (False, None)
+
+ def testFailingWipe(self):
+ node_name = "node13445.example.com"
+ pt = _DiskPauseTracker()
+
+ lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
+ cfg=_ConfigForDiskWipe(node_name))
+
+ disks = [
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
+ size=100 * 1024),
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
+ size=500 * 1024),
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
+ ]
+
+ instance = objects.Instance(name="inst562",
+ primary_node=node_name,
+ disk_template=constants.DT_PLAIN,
+ disks=disks)
+
+ try:
+ cmdlib._WipeDisks(lu, instance)
+ except errors.OpExecError, err:
+ self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
+ else:
+ self.fail("Did not raise exception")
+
+ # Check if all disks were paused and resumed
+ self.assertEqual(pt.history, [
+ ("disk0", 100 * 1024, True),
+ ("disk1", 500 * 1024, True),
+ ("disk2", 256, True),
+ ("disk0", 100 * 1024, False),
+ ("disk1", 500 * 1024, False),
+ ("disk2", 256, False),
+ ])
+
+ def _PrepareWipeTest(self, start_offset, disks):
+ node_name = "node-with-offset%s.example.com" % start_offset
+ pauset = _DiskPauseTracker()
+ progresst = _DiskWipeProgressTracker(start_offset)
+
+ lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
+ cfg=_ConfigForDiskWipe(node_name))
+
+ instance = objects.Instance(name="inst3560",
+ primary_node=node_name,
+ disk_template=constants.DT_PLAIN,
+ disks=disks)
+
+ return (lu, instance, pauset, progresst)
+
+ def testNormalWipe(self):
+ disks = [
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
+ size=500 * 1024),
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
+ size=constants.MAX_WIPE_CHUNK),
+ ]
+
+ (lu, instance, pauset, progresst) = self._PrepareWipeTest(0, disks)
+
+ cmdlib._WipeDisks(lu, instance)
+
+ self.assertEqual(pauset.history, [
+ ("disk0", 1024, True),
+ ("disk1", 500 * 1024, True),
+ ("disk2", 128, True),
+ ("disk3", constants.MAX_WIPE_CHUNK, True),
+ ("disk0", 1024, False),
+ ("disk1", 500 * 1024, False),
+ ("disk2", 128, False),
+ ("disk3", constants.MAX_WIPE_CHUNK, False),
+ ])
+
+ # Ensure the complete disk has been wiped
+ self.assertEqual(progresst.progress,
+ dict((i.logical_id, i.size) for i in disks))
+
+ def testWipeWithStartOffset(self):
+ for start_offset in [0, 280, 8895, 1563204]:
+ disks = [
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
+ size=128),
+ objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
+ size=start_offset + (100 * 1024)),
+ ]
+
+ (lu, instance, pauset, progresst) = \
+ self._PrepareWipeTest(start_offset, disks)
+
+ # Test start offset with only one disk
+ cmdlib._WipeDisks(lu, instance,
+ disks=[(1, disks[1], start_offset)])
+
+ # Only the second disk may have been paused and wiped
+ self.assertEqual(pauset.history, [
+ ("disk1", start_offset + (100 * 1024), True),
+ ("disk1", start_offset + (100 * 1024), False),
+ ])
+ self.assertEqual(progresst.progress, {
+ "disk1": disks[1].size,
+ })
+
+
+class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
+ def testLessThanOneMebibyte(self):
+ for i in [1, 2, 7, 512, 1000, 1023]:
+ lu = _FakeLU()
+ result = cmdlib._DiskSizeInBytesToMebibytes(lu, i)
+ self.assertEqual(result, 1)
+ self.assertEqual(len(lu.warning_log), 1)
+ self.assertEqual(len(lu.warning_log[0]), 2)
+ (_, (warnsize, )) = lu.warning_log[0]
+ self.assertEqual(warnsize, (1024 * 1024) - i)
+
+ def testEven(self):
+ for i in [1, 2, 7, 512, 1000, 1023]:
+ lu = _FakeLU()
+ result = cmdlib._DiskSizeInBytesToMebibytes(lu, i * 1024 * 1024)
+ self.assertEqual(result, i)
+ self.assertFalse(lu.warning_log)
+
+ def testLargeNumber(self):
+ for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
+ for j in [1, 2, 486, 326, 986, 1023]:
+ lu = _FakeLU()
+ size = (1024 * 1024 * i) + j
+ result = cmdlib._DiskSizeInBytesToMebibytes(lu, size)
+ self.assertEqual(result, i + 1, msg="Amount was not rounded up")
+ self.assertEqual(len(lu.warning_log), 1)
+ self.assertEqual(len(lu.warning_log[0]), 2)
+ (_, (warnsize, )) = lu.warning_log[0]
+ self.assertEqual(warnsize, (1024 * 1024) - j)
+
+
if __name__ == "__main__":
testutils.GanetiTestProgram()