blob: 2c827a7f208734504e3fd606e9f17cb1c1952b1e [file] [log] [blame]
#!/usr/bin/python
#
# Copyright (C) 2011 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Script for testing ganeti.client.gnt_cluster"""
import unittest
import optparse
import os
import shutil
import tempfile
from ganeti import errors
from ganeti.client import gnt_cluster
from ganeti import utils
from ganeti import compat
from ganeti import constants
from ganeti import ssh
from ganeti import cli
import mock
import testutils
class TestEpoUtilities(unittest.TestCase):
def setUp(self):
self.nodes2ip = dict(("node%s" % i, "192.0.2.%s" % i) for i in range(1, 10))
self.nodes = set(self.nodes2ip.keys())
self.ips2node = dict((v, k) for (k, v) in self.nodes2ip.items())
def _FakeAction(*args):
return True
def _FakePing(ip, port, live_port_needed=False):
self.assert_(live_port_needed)
self.assertEqual(port, 0)
return True
def _FakeSleep(secs):
self.assert_(secs >= 0 and secs <= 5)
return
def _NoopFeedback(self, text):
return
def testPingFnRemoveHostsUp(self):
seen = set()
def _FakeSeenPing(ip, *args, **kwargs):
node = self.ips2node[ip]
self.assertFalse(node in seen)
seen.add(node)
return True
helper = gnt_cluster._RunWhenNodesReachableHelper(self.nodes,
self._FakeAction,
self.nodes2ip, 0,
self._NoopFeedback,
_ping_fn=_FakeSeenPing,
_sleep_fn=self._FakeSleep)
nodes_len = len(self.nodes)
for (num, _) in enumerate(self.nodes):
helper.Wait(5)
if num < nodes_len - 1:
self.assertRaises(utils.RetryAgain, helper)
else:
helper()
self.assertEqual(seen, self.nodes)
self.assertFalse(helper.down)
self.assertEqual(helper.up, self.nodes)
def testActionReturnFalseSetsHelperFalse(self):
called = False
def _FalseAction(*args):
return called
helper = gnt_cluster._RunWhenNodesReachableHelper(self.nodes, _FalseAction,
self.nodes2ip, 0,
self._NoopFeedback,
_ping_fn=self._FakePing,
_sleep_fn=self._FakeSleep)
for _ in self.nodes:
try:
helper()
except utils.RetryAgain:
called = True
self.assertFalse(helper.success)
def testMaybeInstanceStartup(self):
instances_arg = []
def _FakeInstanceStart(opts, instances, start):
instances_arg.append(set(instances))
return None
inst_map = {
"inst1": set(["node1", "node2"]),
"inst2": set(["node1", "node3"]),
"inst3": set(["node2", "node1"]),
"inst4": set(["node2", "node1", "node3"]),
"inst5": set(["node4"]),
}
fn = _FakeInstanceStart
self.assert_(gnt_cluster._MaybeInstanceStartup(None, inst_map, set(),
_instance_start_fn=fn))
self.assertFalse(instances_arg)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map, set(["node1"]),
_instance_start_fn=fn)
self.assert_(result)
self.assertFalse(instances_arg)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3"]),
_instance_start_fn=fn)
self.assert_(result is None)
self.assertEqual(instances_arg.pop(0), set(["inst2"]))
self.assertFalse("inst2" in inst_map)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3"]),
_instance_start_fn=fn)
self.assert_(result)
self.assertFalse(instances_arg)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3", "node2"]),
_instance_start_fn=fn)
self.assertEqual(instances_arg.pop(0), set(["inst1", "inst3", "inst4"]))
self.assert_(result is None)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3", "node2",
"node4"]),
_instance_start_fn=fn)
self.assert_(result is None)
self.assertEqual(instances_arg.pop(0), set(["inst5"]))
self.assertFalse(inst_map)
class _ClientForEpo:
def __init__(self, groups, nodes):
self._groups = groups
self._nodes = nodes
def QueryGroups(self, names, fields, use_locking):
assert not use_locking
assert fields == ["node_list"]
return self._groups
def QueryNodes(self, names, fields, use_locking):
assert not use_locking
assert fields == ["name", "master", "pinst_list", "sinst_list", "powered",
"offline"]
return self._nodes
class TestEpo(unittest.TestCase):
_ON_EXITCODE = 253
_OFF_EXITCODE = 254
def _ConfirmForce(self, *args):
self.fail("Shouldn't need confirmation")
def _Confirm(self, exp_names, result, names, ltype, text):
self.assertEqual(names, exp_names)
self.assertFalse(result is NotImplemented)
return result
def _Off(self, exp_node_list, opts, node_list, inst_map):
self.assertEqual(node_list, exp_node_list)
self.assertFalse(inst_map)
return self._OFF_EXITCODE
def _Test(self, *args, **kwargs):
defaults = dict(qcl=NotImplemented, _on_fn=NotImplemented,
_off_fn=NotImplemented,
_stdout_fn=lambda *args: None,
_stderr_fn=lambda *args: None)
defaults.update(kwargs)
return gnt_cluster.Epo(*args, **defaults)
def testShowAllWithGroups(self):
opts = optparse.Values(dict(groups=True, show_all=True))
result = self._Test(opts, NotImplemented)
self.assertEqual(result, constants.EXIT_FAILURE)
def testShowAllWithArgs(self):
opts = optparse.Values(dict(groups=False, show_all=True))
result = self._Test(opts, ["a", "b", "c"])
self.assertEqual(result, constants.EXIT_FAILURE)
def testNoArgumentsNoParameters(self):
for (force, confirm_result) in [(True, NotImplemented), (False, False),
(False, True)]:
opts = optparse.Values(dict(groups=False, show_all=False, force=force,
on=False))
client = _ClientForEpo(NotImplemented, [
("node1.example.com", False, [], [], True, False),
])
if force:
confirm_fn = self._ConfirmForce
else:
confirm_fn = compat.partial(self._Confirm, ["node1.example.com"],
confirm_result)
off_fn = compat.partial(self._Off, ["node1.example.com"])
result = self._Test(opts, [], qcl=client, _off_fn=off_fn,
_confirm_fn=confirm_fn)
if force or confirm_result:
self.assertEqual(result, self._OFF_EXITCODE)
else:
self.assertEqual(result, constants.EXIT_FAILURE)
def testPowerOn(self):
for master in [False, True]:
opts = optparse.Values(dict(groups=False, show_all=True,
force=True, on=True))
client = _ClientForEpo(NotImplemented, [
("node1.example.com", False, [], [], True, False),
("node2.example.com", False, [], [], False, False),
("node3.example.com", False, [], [], True, True),
("node4.example.com", False, [], [], None, True),
("node5.example.com", master, [], [], False, False),
])
def _On(_, all_nodes, node_list, inst_map):
self.assertEqual(all_nodes,
["node%s.example.com" % i for i in range(1, 6)])
if master:
self.assertEqual(node_list, ["node2.example.com"])
else:
self.assertEqual(node_list, ["node2.example.com",
"node5.example.com"])
self.assertFalse(inst_map)
return self._ON_EXITCODE
result = self._Test(opts, [], qcl=client, _on_fn=_On,
_confirm_fn=self._ConfirmForce)
self.assertEqual(result, self._ON_EXITCODE)
def testMasterWithoutShowAll(self):
opts = optparse.Values(dict(groups=False, show_all=False,
force=True, on=False))
client = _ClientForEpo(NotImplemented, [
("node1.example.com", True, [], [], True, False),
])
result = self._Test(opts, [], qcl=client, _confirm_fn=self._ConfirmForce)
self.assertEqual(result, constants.EXIT_FAILURE)
class DrbdHelperTestCase(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.enabled_disk_templates = []
def enableDrbd(self):
self.enabled_disk_templates = [constants.DT_DRBD8]
def disableDrbd(self):
self.enabled_disk_templates = [constants.DT_DISKLESS]
class InitDrbdHelper(DrbdHelperTestCase):
def testNoDrbdNoHelper(self):
opts = mock.Mock()
opts.drbd_helper = None
self.disableDrbd()
helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
feedback_fn=mock.Mock())
self.assertEquals(None, helper)
def testNoDrbdHelper(self):
opts = mock.Mock()
self.disableDrbd()
opts.drbd_helper = "/bin/true"
helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
feedback_fn=mock.Mock())
self.assertEquals(opts.drbd_helper, helper)
def testDrbdHelperNone(self):
opts = mock.Mock()
self.enableDrbd()
opts.drbd_helper = None
helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
feedback_fn=mock.Mock())
self.assertEquals(constants.DEFAULT_DRBD_HELPER, helper)
def testDrbdHelperEmpty(self):
opts = mock.Mock()
self.enableDrbd()
opts.drbd_helper = ''
self.assertRaises(errors.OpPrereqError, gnt_cluster._InitDrbdHelper, opts,
self.enabled_disk_templates, feedback_fn=mock.Mock())
def testDrbdHelper(self):
opts = mock.Mock()
self.enableDrbd()
opts.drbd_helper = "/bin/true"
helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
feedback_fn=mock.Mock())
self.assertEquals(opts.drbd_helper, helper)
class GetDrbdHelper(DrbdHelperTestCase):
def testNoDrbdNoHelper(self):
opts = mock.Mock()
self.disableDrbd()
opts.drbd_helper = None
helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
self.assertEquals(None, helper)
def testNoTemplateInfoNoHelper(self):
opts = mock.Mock()
opts.drbd_helper = None
helper = gnt_cluster._GetDrbdHelper(opts, None)
self.assertEquals(None, helper)
def testNoTemplateInfoHelper(self):
opts = mock.Mock()
opts.drbd_helper = "/bin/true"
helper = gnt_cluster._GetDrbdHelper(opts, None)
self.assertEquals(opts.drbd_helper, helper)
def testNoDrbdHelper(self):
opts = mock.Mock()
self.disableDrbd()
opts.drbd_helper = "/bin/true"
helper = gnt_cluster._GetDrbdHelper(opts, None)
self.assertEquals(opts.drbd_helper, helper)
def testDrbdNoHelper(self):
opts = mock.Mock()
self.enableDrbd()
opts.drbd_helper = None
helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
self.assertEquals(None, helper)
def testDrbdHelper(self):
opts = mock.Mock()
self.enableDrbd()
opts.drbd_helper = "/bin/true"
helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
self.assertEquals(opts.drbd_helper, helper)
class TestBuildGanetiPubKeys(testutils.GanetiTestCase):
_SOME_KEY_DICT = {"rsa": "key_rsa",
"dsa": "key_dsa"}
_MASTER_NODE_NAME = "master_node"
_MASTER_NODE_UUID = "master_uuid"
_NUM_NODES = 2 # excluding master node
_ONLINE_NODE_NAMES = ["node%s_name" % i for i in range(_NUM_NODES)]
_ONLINE_NODE_UUIDS = ["node%s_uuid" % i for i in range(_NUM_NODES)]
_CLUSTER_NAME = "cluster_name"
_PRIV_KEY = "master_private_key"
_PUB_KEY = "master_public_key"
_MODIFY_SSH_SETUP = True
_AUTH_KEYS = "a\nb\nc"
_SSH_KEY_TYPE = "dsa"
def _setUpFakeKeys(self):
os.makedirs(os.path.join(self.tmpdir, ".ssh"))
for key_type in ["rsa", "dsa"]:
self.priv_filename = os.path.join(self.tmpdir, ".ssh", "id_%s" % key_type)
utils.WriteFile(self.priv_filename, data=self._PRIV_KEY)
self.pub_filename = os.path.join(
self.tmpdir, ".ssh", "id_%s.pub" % key_type)
utils.WriteFile(self.pub_filename, data=self._PUB_KEY)
self.auth_filename = os.path.join(self.tmpdir, ".ssh", "authorized_keys")
utils.WriteFile(self.auth_filename, data=self._AUTH_KEYS)
def setUp(self):
testutils.GanetiTestCase.setUp(self)
self.tmpdir = tempfile.mkdtemp()
self.pub_key_filename = os.path.join(self.tmpdir, "ganeti_test_pub_keys")
self._setUpFakeKeys()
self._ssh_read_remote_ssh_pub_keys_patcher = testutils \
.patch_object(ssh, "ReadRemoteSshPubKey")
self._ssh_read_remote_ssh_pub_keys_mock = \
self._ssh_read_remote_ssh_pub_keys_patcher.start()
self._ssh_read_remote_ssh_pub_keys_mock.return_value = self._SOME_KEY_DICT
self.mock_cl = mock.Mock()
self.mock_cl.QueryConfigValues = mock.Mock()
self.mock_cl.QueryConfigValues.return_value = \
(self._CLUSTER_NAME, self._MASTER_NODE_NAME, self._MODIFY_SSH_SETUP,
self._SSH_KEY_TYPE)
self._get_online_nodes_mock = mock.Mock()
self._get_online_nodes_mock.return_value = \
self._ONLINE_NODE_NAMES
self._get_nodes_ssh_ports_mock = mock.Mock()
self._get_nodes_ssh_ports_mock.return_value = \
[22 for i in range(self._NUM_NODES + 1)]
self._get_node_uuids_mock = mock.Mock()
self._get_node_uuids_mock.return_value = \
self._ONLINE_NODE_UUIDS + [self._MASTER_NODE_UUID]
self._options = mock.Mock()
self._options.ssh_key_check = False
def _GetTempHomedir(self, _):
return self.tmpdir
def tearDown(self):
super(testutils.GanetiTestCase, self).tearDown()
shutil.rmtree(self.tmpdir)
self._ssh_read_remote_ssh_pub_keys_patcher.stop()
def testNewPubKeyFile(self):
gnt_cluster._BuildGanetiPubKeys(
self._options,
pub_key_file=self.pub_key_filename,
cl=self.mock_cl,
get_online_nodes_fn=self._get_online_nodes_mock,
get_nodes_ssh_ports_fn=self._get_nodes_ssh_ports_mock,
get_node_uuids_fn=self._get_node_uuids_mock,
homedir_fn=self._GetTempHomedir)
key_file_result = utils.ReadFile(self.pub_key_filename)
for node_uuid in self._ONLINE_NODE_UUIDS + [self._MASTER_NODE_UUID]:
self.assertTrue(node_uuid in key_file_result)
self.assertTrue(self._PUB_KEY in key_file_result)
def testOverridePubKeyFile(self):
fd = open(self.pub_key_filename, "w")
fd.write("Pink Bunny")
fd.close()
gnt_cluster._BuildGanetiPubKeys(
self._options,
pub_key_file=self.pub_key_filename,
cl=self.mock_cl,
get_online_nodes_fn=self._get_online_nodes_mock,
get_nodes_ssh_ports_fn=self._get_nodes_ssh_ports_mock,
get_node_uuids_fn=self._get_node_uuids_mock,
homedir_fn=self._GetTempHomedir)
self.assertFalse("Pink Bunny" in self.pub_key_filename)
if __name__ == "__main__":
testutils.GanetiTestProgram()