4 # Copyright (C) 2011 Google Inc.
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 """Script for testing ganeti.client.gnt_cluster"""
39 from ganeti
import errors
40 from ganeti
.client
import gnt_cluster
41 from ganeti
import utils
42 from ganeti
import compat
43 from ganeti
import constants
44 from ganeti
import ssh
45 from ganeti
import cli
51 class TestEpoUtilities(unittest
.TestCase
):
53 self
.nodes2ip
= dict(("node%s" % i
, "192.0.2.%s" % i
) for i
in range(1, 10))
54 self
.nodes
= set(self
.nodes2ip
.keys())
55 self
.ips2node
= dict((v
, k
) for (k
, v
) in self
.nodes2ip
.items())
57 def _FakeAction(*args
):
60 def _FakePing(ip
, port
, live_port_needed
=False):
61 self
.assert_(live_port_needed
)
62 self
.assertEqual(port
, 0)
66 self
.assert_(secs
>= 0 and secs
<= 5)
69 def _NoopFeedback(self
, text
):
72 def testPingFnRemoveHostsUp(self
):
74 def _FakeSeenPing(ip
, *args
, **kwargs
):
75 node
= self
.ips2node
[ip
]
76 self
.assertFalse(node
in seen
)
80 helper
= gnt_cluster
._RunWhenNodesReachableHelper(self
.nodes
,
84 _ping_fn
=_FakeSeenPing
,
85 _sleep_fn
=self
._FakeSleep
)
87 nodes_len
= len(self
.nodes
)
88 for (num
, _
) in enumerate(self
.nodes
):
90 if num
< nodes_len
- 1:
91 self
.assertRaises(utils
.RetryAgain
, helper
)
95 self
.assertEqual(seen
, self
.nodes
)
96 self
.assertFalse(helper
.down
)
97 self
.assertEqual(helper
.up
, self
.nodes
)
99 def testActionReturnFalseSetsHelperFalse(self
):
101 def _FalseAction(*args
):
104 helper
= gnt_cluster
._RunWhenNodesReachableHelper(self
.nodes
, _FalseAction
,
107 _ping_fn
=self
._FakePing
,
108 _sleep_fn
=self
._FakeSleep
)
112 except utils
.RetryAgain
:
115 self
.assertFalse(helper
.success
)
117 def testMaybeInstanceStartup(self
):
119 def _FakeInstanceStart(opts
, instances
, start
):
120 instances_arg
.append(set(instances
))
124 "inst1": set(["node1", "node2"]),
125 "inst2": set(["node1", "node3"]),
126 "inst3": set(["node2", "node1"]),
127 "inst4": set(["node2", "node1", "node3"]),
128 "inst5": set(["node4"]),
131 fn
= _FakeInstanceStart
132 self
.assert_(gnt_cluster
._MaybeInstanceStartup(None, inst_map
, set(),
133 _instance_start_fn
=fn
))
134 self
.assertFalse(instances_arg
)
135 result
= gnt_cluster
._MaybeInstanceStartup(None, inst_map
, set(["node1"]),
136 _instance_start_fn
=fn
)
138 self
.assertFalse(instances_arg
)
139 result
= gnt_cluster
._MaybeInstanceStartup(None, inst_map
,
140 set(["node1", "node3"]),
141 _instance_start_fn
=fn
)
142 self
.assert_(result
is None)
143 self
.assertEqual(instances_arg
.pop(0), set(["inst2"]))
144 self
.assertFalse("inst2" in inst_map
)
145 result
= gnt_cluster
._MaybeInstanceStartup(None, inst_map
,
146 set(["node1", "node3"]),
147 _instance_start_fn
=fn
)
149 self
.assertFalse(instances_arg
)
150 result
= gnt_cluster
._MaybeInstanceStartup(None, inst_map
,
151 set(["node1", "node3", "node2"]),
152 _instance_start_fn
=fn
)
153 self
.assertEqual(instances_arg
.pop(0), set(["inst1", "inst3", "inst4"]))
154 self
.assert_(result
is None)
155 result
= gnt_cluster
._MaybeInstanceStartup(None, inst_map
,
156 set(["node1", "node3", "node2",
158 _instance_start_fn
=fn
)
159 self
.assert_(result
is None)
160 self
.assertEqual(instances_arg
.pop(0), set(["inst5"]))
161 self
.assertFalse(inst_map
)
165 def __init__(self
, groups
, nodes
):
166 self
._groups
= groups
169 def QueryGroups(self
, names
, fields
, use_locking
):
170 assert not use_locking
171 assert fields
== ["node_list"]
174 def QueryNodes(self
, names
, fields
, use_locking
):
175 assert not use_locking
176 assert fields
== ["name", "master", "pinst_list", "sinst_list", "powered",
181 class TestEpo(unittest
.TestCase
):
185 def _ConfirmForce(self
, *args
):
186 self
.fail("Shouldn't need confirmation")
188 def _Confirm(self
, exp_names
, result
, names
, ltype
, text
):
189 self
.assertEqual(names
, exp_names
)
190 self
.assertFalse(result
is NotImplemented)
193 def _Off(self
, exp_node_list
, opts
, node_list
, inst_map
):
194 self
.assertEqual(node_list
, exp_node_list
)
195 self
.assertFalse(inst_map
)
196 return self
._OFF_EXITCODE
198 def _Test(self
, *args
, **kwargs
):
199 defaults
= dict(qcl
=NotImplemented, _on_fn
=NotImplemented,
200 _off_fn
=NotImplemented,
201 _stdout_fn
=lambda *args
: None,
202 _stderr_fn
=lambda *args
: None)
203 defaults
.update(kwargs
)
204 return gnt_cluster
.Epo(*args
, **defaults
)
206 def testShowAllWithGroups(self
):
207 opts
= optparse
.Values(dict(groups
=True, show_all
=True))
208 result
= self
._Test(opts
, NotImplemented)
209 self
.assertEqual(result
, constants
.EXIT_FAILURE
)
211 def testShowAllWithArgs(self
):
212 opts
= optparse
.Values(dict(groups
=False, show_all
=True))
213 result
= self
._Test(opts
, ["a", "b", "c"])
214 self
.assertEqual(result
, constants
.EXIT_FAILURE
)
216 def testNoArgumentsNoParameters(self
):
217 for (force
, confirm_result
) in [(True, NotImplemented), (False, False),
219 opts
= optparse
.Values(dict(groups
=False, show_all
=False, force
=force
,
221 client
= _ClientForEpo(NotImplemented, [
222 ("node1.example.com", False, [], [], True, False),
226 confirm_fn
= self
._ConfirmForce
228 confirm_fn
= compat
.partial(self
._Confirm
, ["node1.example.com"],
231 off_fn
= compat
.partial(self
._Off
, ["node1.example.com"])
233 result
= self
._Test(opts
, [], qcl
=client
, _off_fn
=off_fn
,
234 _confirm_fn
=confirm_fn
)
235 if force
or confirm_result
:
236 self
.assertEqual(result
, self
._OFF_EXITCODE
)
238 self
.assertEqual(result
, constants
.EXIT_FAILURE
)
240 def testPowerOn(self
):
241 for master
in [False, True]:
242 opts
= optparse
.Values(dict(groups
=False, show_all
=True,
243 force
=True, on
=True))
244 client
= _ClientForEpo(NotImplemented, [
245 ("node1.example.com", False, [], [], True, False),
246 ("node2.example.com", False, [], [], False, False),
247 ("node3.example.com", False, [], [], True, True),
248 ("node4.example.com", False, [], [], None, True),
249 ("node5.example.com", master
, [], [], False, False),
252 def _On(_
, all_nodes
, node_list
, inst_map
):
253 self
.assertEqual(all_nodes
,
254 ["node%s.example.com" % i
for i
in range(1, 6)])
256 self
.assertEqual(node_list
, ["node2.example.com"])
258 self
.assertEqual(node_list
, ["node2.example.com",
259 "node5.example.com"])
260 self
.assertFalse(inst_map
)
261 return self
._ON_EXITCODE
263 result
= self
._Test(opts
, [], qcl
=client
, _on_fn
=_On
,
264 _confirm_fn
=self
._ConfirmForce
)
265 self
.assertEqual(result
, self
._ON_EXITCODE
)
267 def testMasterWithoutShowAll(self
):
268 opts
= optparse
.Values(dict(groups
=False, show_all
=False,
269 force
=True, on
=False))
270 client
= _ClientForEpo(NotImplemented, [
271 ("node1.example.com", True, [], [], True, False),
273 result
= self
._Test(opts
, [], qcl
=client
, _confirm_fn
=self
._ConfirmForce
)
274 self
.assertEqual(result
, constants
.EXIT_FAILURE
)
277 class DrbdHelperTestCase(unittest
.TestCase
):
280 unittest
.TestCase
.setUp(self
)
281 self
.enabled_disk_templates
= []
283 def enableDrbd(self
):
284 self
.enabled_disk_templates
= [constants
.DT_DRBD8
]
286 def disableDrbd(self
):
287 self
.enabled_disk_templates
= [constants
.DT_DISKLESS
]
290 class InitDrbdHelper(DrbdHelperTestCase
):
292 def testNoDrbdNoHelper(self
):
294 opts
.drbd_helper
= None
296 helper
= gnt_cluster
._InitDrbdHelper(opts
, self
.enabled_disk_templates
,
297 feedback_fn
=mock
.Mock())
298 self
.assertEquals(None, helper
)
300 def testNoDrbdHelper(self
):
303 opts
.drbd_helper
= "/bin/true"
304 helper
= gnt_cluster
._InitDrbdHelper(opts
, self
.enabled_disk_templates
,
305 feedback_fn
=mock
.Mock())
306 self
.assertEquals(opts
.drbd_helper
, helper
)
308 def testDrbdHelperNone(self
):
311 opts
.drbd_helper
= None
312 helper
= gnt_cluster
._InitDrbdHelper(opts
, self
.enabled_disk_templates
,
313 feedback_fn
=mock
.Mock())
314 self
.assertEquals(constants
.DEFAULT_DRBD_HELPER
, helper
)
316 def testDrbdHelperEmpty(self
):
319 opts
.drbd_helper
= ''
320 self
.assertRaises(errors
.OpPrereqError
, gnt_cluster
._InitDrbdHelper
, opts
,
321 self
.enabled_disk_templates
, feedback_fn
=mock
.Mock())
323 def testDrbdHelper(self
):
326 opts
.drbd_helper
= "/bin/true"
327 helper
= gnt_cluster
._InitDrbdHelper(opts
, self
.enabled_disk_templates
,
328 feedback_fn
=mock
.Mock())
329 self
.assertEquals(opts
.drbd_helper
, helper
)
332 class GetDrbdHelper(DrbdHelperTestCase
):
334 def testNoDrbdNoHelper(self
):
337 opts
.drbd_helper
= None
338 helper
= gnt_cluster
._GetDrbdHelper(opts
, self
.enabled_disk_templates
)
339 self
.assertEquals(None, helper
)
341 def testNoTemplateInfoNoHelper(self
):
343 opts
.drbd_helper
= None
344 helper
= gnt_cluster
._GetDrbdHelper(opts
, None)
345 self
.assertEquals(None, helper
)
347 def testNoTemplateInfoHelper(self
):
349 opts
.drbd_helper
= "/bin/true"
350 helper
= gnt_cluster
._GetDrbdHelper(opts
, None)
351 self
.assertEquals(opts
.drbd_helper
, helper
)
353 def testNoDrbdHelper(self
):
356 opts
.drbd_helper
= "/bin/true"
357 helper
= gnt_cluster
._GetDrbdHelper(opts
, None)
358 self
.assertEquals(opts
.drbd_helper
, helper
)
360 def testDrbdNoHelper(self
):
363 opts
.drbd_helper
= None
364 helper
= gnt_cluster
._GetDrbdHelper(opts
, self
.enabled_disk_templates
)
365 self
.assertEquals(None, helper
)
367 def testDrbdHelper(self
):
370 opts
.drbd_helper
= "/bin/true"
371 helper
= gnt_cluster
._GetDrbdHelper(opts
, self
.enabled_disk_templates
)
372 self
.assertEquals(opts
.drbd_helper
, helper
)
375 class TestBuildGanetiPubKeys(testutils
.GanetiTestCase
):
377 _SOME_KEY_DICT
= {"rsa": "key_rsa",
379 _MASTER_NODE_NAME
= "master_node"
380 _MASTER_NODE_UUID
= "master_uuid"
381 _NUM_NODES
= 2 # excluding master node
382 _ONLINE_NODE_NAMES
= ["node%s_name" % i
for i
in range(_NUM_NODES
)]
383 _ONLINE_NODE_UUIDS
= ["node%s_uuid" % i
for i
in range(_NUM_NODES
)]
384 _CLUSTER_NAME
= "cluster_name"
385 _PRIV_KEY
= "master_private_key"
386 _PUB_KEY
= "master_public_key"
387 _AUTH_KEYS
= "a\nb\nc"
389 def _setUpFakeKeys(self
):
390 os
.makedirs(os
.path
.join(self
.tmpdir
, ".ssh"))
392 for key_type
in ["rsa", "dsa"]:
393 self
.priv_filename
= os
.path
.join(self
.tmpdir
, ".ssh", "id_%s" % key_type
)
394 utils
.WriteFile(self
.priv_filename
, data
=self
._PRIV_KEY
)
396 self
.pub_filename
= os
.path
.join(
397 self
.tmpdir
, ".ssh", "id_%s.pub" % key_type
)
398 utils
.WriteFile(self
.pub_filename
, data
=self
._PUB_KEY
)
400 self
.auth_filename
= os
.path
.join(self
.tmpdir
, ".ssh", "authorized_keys")
401 utils
.WriteFile(self
.auth_filename
, data
=self
._AUTH_KEYS
)
404 testutils
.GanetiTestCase
.setUp(self
)
405 self
.tmpdir
= tempfile
.mkdtemp()
406 self
.pub_key_filename
= os
.path
.join(self
.tmpdir
, "ganeti_test_pub_keys")
407 self
._setUpFakeKeys()
409 self
._ssh_read_remote_ssh_pub_keys_patcher
= testutils \
410 .patch_object(ssh
, "ReadRemoteSshPubKeys")
411 self
._ssh_read_remote_ssh_pub_keys_mock
= \
412 self
._ssh_read_remote_ssh_pub_keys_patcher
.start()
413 self
._ssh_read_remote_ssh_pub_keys_mock
.return_value
= self
._SOME_KEY_DICT
415 self
.mock_cl
= mock
.Mock()
416 self
.mock_cl
.QueryConfigValues
= mock
.Mock()
417 self
.mock_cl
.QueryConfigValues
.return_value
= \
418 (self
._CLUSTER_NAME
, self
._MASTER_NODE_NAME
)
420 self
._get_online_nodes_mock
= mock
.Mock()
421 self
._get_online_nodes_mock
.return_value
= \
422 self
._ONLINE_NODE_NAMES
424 self
._get_nodes_ssh_ports_mock
= mock
.Mock()
425 self
._get_nodes_ssh_ports_mock
.return_value
= \
426 [22 for i
in range(self
._NUM_NODES
+ 1)]
428 self
._get_node_uuids_mock
= mock
.Mock()
429 self
._get_node_uuids_mock
.return_value
= \
430 self
._ONLINE_NODE_UUIDS
+ [self
._MASTER_NODE_UUID
]
432 self
._options
= mock
.Mock()
433 self
._options
.ssh_key_check
= False
435 def _GetTempHomedir(self
, _
):
439 super(testutils
.GanetiTestCase
, self
).tearDown()
440 shutil
.rmtree(self
.tmpdir
)
441 self
._ssh_read_remote_ssh_pub_keys_patcher
.stop()
443 def testNewPubKeyFile(self
):
444 gnt_cluster
._BuildGanetiPubKeys(
446 pub_key_file
=self
.pub_key_filename
,
448 get_online_nodes_fn
=self
._get_online_nodes_mock
,
449 get_nodes_ssh_ports_fn
=self
._get_nodes_ssh_ports_mock
,
450 get_node_uuids_fn
=self
._get_node_uuids_mock
,
451 homedir_fn
=self
._GetTempHomedir
)
452 key_file_result
= utils
.ReadFile(self
.pub_key_filename
)
453 for node_uuid
in self
._ONLINE_NODE_UUIDS
+ [self
._MASTER_NODE_UUID
]:
454 self
.assertTrue(node_uuid
in key_file_result
)
455 self
.assertTrue(self
._PUB_KEY
in key_file_result
)
457 def testOverridePubKeyFile(self
):
458 fd
= open(self
.pub_key_filename
, "w")
459 fd
.write("Pink Bunny")
461 gnt_cluster
._BuildGanetiPubKeys(
463 pub_key_file
=self
.pub_key_filename
,
465 get_online_nodes_fn
=self
._get_online_nodes_mock
,
466 get_nodes_ssh_ports_fn
=self
._get_nodes_ssh_ports_mock
,
467 get_node_uuids_fn
=self
._get_node_uuids_mock
,
468 homedir_fn
=self
._GetTempHomedir
)
469 self
.assertFalse("Pink Bunny" in self
.pub_key_filename
)
472 if __name__
== "__main__":
473 testutils
.GanetiTestProgram()