Revision 08cef8fc
b/Makefile.am | ||
---|---|---|
1335 | 1335 |
test/py/cmdlib/testsupport/config_mock.py \ |
1336 | 1336 |
test/py/cmdlib/testsupport/iallocator_mock.py \ |
1337 | 1337 |
test/py/cmdlib/testsupport/lock_manager_mock.py \ |
1338 |
test/py/cmdlib/testsupport/netutils_mock.py \ |
|
1338 | 1339 |
test/py/cmdlib/testsupport/processor_mock.py \ |
1339 |
test/py/cmdlib/testsupport/rpc_runner_mock.py |
|
1340 |
test/py/cmdlib/testsupport/rpc_runner_mock.py \ |
|
1341 |
test/py/cmdlib/testsupport/ssh_mock.py \ |
|
1342 |
test/py/cmdlib/testsupport/util.py |
|
1340 | 1343 |
|
1341 | 1344 |
haskell_tests = test/hs/htest |
1342 | 1345 |
|
b/test/py/cmdlib/cluster_unittest.py | ||
---|---|---|
402 | 402 |
self.ExecOpCode(op) |
403 | 403 |
|
404 | 404 |
|
405 |
class TestLUClusterRename(CmdlibTestCase): |
|
406 |
NEW_NAME = "new-name.example.com" |
|
407 |
NEW_IP = "1.2.3.4" |
|
408 |
|
|
409 |
def testNoChanges(self): |
|
410 |
op = opcodes.OpClusterRename(name=self.cfg.GetClusterName()) |
|
411 |
|
|
412 |
self.ExecOpCodeExpectOpPrereqError(op, "name nor the IP address") |
|
413 |
|
|
414 |
def testReachableIp(self): |
|
415 |
op = opcodes.OpClusterRename(name=self.NEW_NAME) |
|
416 |
|
|
417 |
self.netutils_mod.GetHostname.return_value = \ |
|
418 |
HostnameMock(self.NEW_NAME, self.NEW_IP) |
|
419 |
self.netutils_mod.TcpPing.return_value = True |
|
420 |
|
|
421 |
self.ExecOpCodeExpectOpPrereqError(op, "is reachable on the network") |
|
422 |
|
|
423 |
def testValidRename(self): |
|
424 |
op = opcodes.OpClusterRename(name=self.NEW_NAME) |
|
425 |
|
|
426 |
self.netutils_mod.GetHostname.return_value = \ |
|
427 |
HostnameMock(self.NEW_NAME, self.NEW_IP) |
|
428 |
|
|
429 |
self.ExecOpCode(op) |
|
430 |
|
|
431 |
self.assertEqual(1, self.ssh_mod.WriteKnownHostsFile.call_count) |
|
432 |
self.rpc.call_node_deactivate_master_ip.assert_called_once_with( |
|
433 |
self.cfg.GetMasterNode(), |
|
434 |
self.cfg.GetMasterNetworkParameters(), |
|
435 |
False) |
|
436 |
self.rpc.call_node_activate_master_ip.assert_called_once_with( |
|
437 |
self.cfg.GetMasterNode(), |
|
438 |
self.cfg.GetMasterNetworkParameters(), |
|
439 |
False) |
|
440 |
|
|
441 |
def testRenameOfflineMaster(self): |
|
442 |
op = opcodes.OpClusterRename(name=self.NEW_NAME) |
|
443 |
|
|
444 |
self.cfg.GetMasterNodeInfo().offline = True |
|
445 |
self.netutils_mod.GetHostname.return_value = \ |
|
446 |
HostnameMock(self.NEW_NAME, self.NEW_IP) |
|
447 |
|
|
448 |
self.ExecOpCode(op) |
|
449 |
|
|
450 |
|
|
405 | 451 |
if __name__ == "__main__": |
406 | 452 |
testutils.GanetiTestProgram() |
b/test/py/cmdlib/testsupport/__init__.py | ||
---|---|---|
27 | 27 |
from cmdlib.testsupport.config_mock import ConfigMock |
28 | 28 |
from cmdlib.testsupport.iallocator_mock import patchIAllocator |
29 | 29 |
from cmdlib.testsupport.lock_manager_mock import LockManagerMock |
30 |
from cmdlib.testsupport.netutils_mock import patchNetutils, HostnameMock |
|
30 | 31 |
from cmdlib.testsupport.processor_mock import ProcessorMock |
31 | 32 |
from cmdlib.testsupport.rpc_runner_mock import CreateRpcRunnerMock, \ |
32 | 33 |
RpcResultsBuilder |
34 |
from cmdlib.testsupport.ssh_mock import patchSsh |
|
33 | 35 |
|
34 | 36 |
__all__ = ["CmdlibTestCase", |
35 | 37 |
"ConfigMock", |
36 |
"patchIAllocator", |
|
37 | 38 |
"CreateRpcRunnerMock", |
39 |
"HostnameMock", |
|
40 |
"patchIAllocator", |
|
41 |
"patchNetutils", |
|
42 |
"patchSsh", |
|
38 | 43 |
"LockManagerMock", |
39 | 44 |
"ProcessorMock", |
40 | 45 |
"RpcResultsBuilder", |
b/test/py/cmdlib/testsupport/cmdlib_testcase.py | ||
---|---|---|
29 | 29 |
from cmdlib.testsupport.config_mock import ConfigMock |
30 | 30 |
from cmdlib.testsupport.iallocator_mock import patchIAllocator |
31 | 31 |
from cmdlib.testsupport.lock_manager_mock import LockManagerMock |
32 |
from cmdlib.testsupport.netutils_mock import patchNetutils, \ |
|
33 |
SetupDefaultNetutilsMock |
|
32 | 34 |
from cmdlib.testsupport.processor_mock import ProcessorMock |
33 | 35 |
from cmdlib.testsupport.rpc_runner_mock import CreateRpcRunnerMock |
36 |
from cmdlib.testsupport.ssh_mock import patchSsh |
|
34 | 37 |
|
35 | 38 |
from ganeti import errors |
36 | 39 |
from ganeti import opcodes |
... | ... | |
60 | 63 |
* C{rpc}: @see L{CreateRpcRunnerMock} |
61 | 64 |
* C{iallocator_cls}: @see L{patchIAllocator} |
62 | 65 |
* C{mcpu}: @see L{ProcessorMock} |
66 |
* C{netutils_mod}: @see L{patchNetutils} |
|
67 |
* C{ssh_mod}: @see L{patchSsh} |
|
63 | 68 |
|
64 | 69 |
""" |
65 | 70 |
|
... | ... | |
68 | 73 |
def setUp(self): |
69 | 74 |
super(CmdlibTestCase, self).setUp() |
70 | 75 |
self._iallocator_patcher = None |
76 |
self._netutils_patcher = None |
|
77 |
self._ssh_patcher = None |
|
71 | 78 |
|
72 | 79 |
try: |
73 | 80 |
runtime.InitArchInfo() |
... | ... | |
81 | 88 |
if self._iallocator_patcher is not None: |
82 | 89 |
self._iallocator_patcher.stop() |
83 | 90 |
self._iallocator_patcher = None |
91 |
if self._netutils_patcher is not None: |
|
92 |
self._netutils_patcher.stop() |
|
93 |
self._netutils_patcher = None |
|
94 |
if self._ssh_patcher is not None: |
|
95 |
self._ssh_patcher.stop() |
|
96 |
self._ssh_patcher = None |
|
84 | 97 |
|
85 | 98 |
def tearDown(self): |
86 | 99 |
super(CmdlibTestCase, self).tearDown() |
... | ... | |
105 | 118 |
self.cfg = ConfigMock() |
106 | 119 |
self.glm = LockManagerMock() |
107 | 120 |
self.rpc = CreateRpcRunnerMock() |
121 |
ctx = GanetiContextMock(self.cfg, self.glm, self.rpc) |
|
122 |
self.mcpu = ProcessorMock(ctx) |
|
108 | 123 |
|
109 | 124 |
self._StopPatchers() |
110 | 125 |
try: |
111 | 126 |
self._iallocator_patcher = patchIAllocator(self._GetTestModule()) |
112 | 127 |
self.iallocator_cls = self._iallocator_patcher.start() |
113 |
except ImportError:
|
|
128 |
except (ImportError, AttributeError):
|
|
114 | 129 |
# this test module does not use iallocator, no patching performed |
115 | 130 |
self._iallocator_patcher = None |
116 | 131 |
|
117 |
ctx = GanetiContextMock(self.cfg, self.glm, self.rpc) |
|
118 |
self.mcpu = ProcessorMock(ctx) |
|
132 |
try: |
|
133 |
self._netutils_patcher = patchNetutils(self._GetTestModule()) |
|
134 |
self.netutils_mod = self._netutils_patcher.start() |
|
135 |
SetupDefaultNetutilsMock(self.netutils_mod, self.cfg) |
|
136 |
except (ImportError, AttributeError): |
|
137 |
# this test module does not use netutils, no patching performed |
|
138 |
self._netutils_patcher = None |
|
139 |
|
|
140 |
try: |
|
141 |
self._ssh_patcher = patchSsh(self._GetTestModule()) |
|
142 |
self.ssh_mod = self._ssh_patcher.start() |
|
143 |
except (ImportError, AttributeError): |
|
144 |
# this test module does not use ssh, no patching performed |
|
145 |
self._ssh_patcher = None |
|
119 | 146 |
|
120 | 147 |
def ExecOpCode(self, opcode): |
121 | 148 |
"""Executes the given opcode. |
b/test/py/cmdlib/testsupport/iallocator_mock.py | ||
---|---|---|
22 | 22 |
"""Support for mocking the IAllocator interface""" |
23 | 23 |
|
24 | 24 |
|
25 |
import mock
|
|
25 |
from cmdlib.testsupport.util import patchModule
|
|
26 | 26 |
|
27 | 27 |
|
28 | 28 |
# pylint: disable=C0103 |
... | ... | |
36 | 36 |
"ganeti.cmdlib" prefix is optional. |
37 | 37 |
|
38 | 38 |
""" |
39 |
if not module_under_test.startswith("ganeti.cmdlib"): |
|
40 |
module_under_test = "ganeti.cmdlib." + module_under_test |
|
41 |
return mock.patch("%s.iallocator.IAllocator" % module_under_test) |
|
39 |
return patchModule(module_under_test, "iallocator.IAllocator") |
b/test/py/cmdlib/testsupport/netutils_mock.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2013 Google Inc. |
|
5 |
# |
|
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. |
|
10 |
# |
|
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. |
|
15 |
# |
|
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 |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Support for mocking the netutils module""" |
|
23 |
|
|
24 |
import mock |
|
25 |
|
|
26 |
|
|
27 |
from ganeti import compat |
|
28 |
from ganeti import errors |
|
29 |
from ganeti import netutils |
|
30 |
from cmdlib.testsupport.util import patchModule |
|
31 |
|
|
32 |
|
|
33 |
# pylint: disable=C0103 |
|
34 |
def patchNetutils(module_under_test): |
|
35 |
"""Patches the L{ganeti.netutils} module for tests. |
|
36 |
|
|
37 |
This function is meant to be used as a decorator for test methods. |
|
38 |
|
|
39 |
@type module_under_test: string |
|
40 |
@param module_under_test: the module within cmdlib which is tested. The |
|
41 |
"ganeti.cmdlib" prefix is optional. |
|
42 |
|
|
43 |
""" |
|
44 |
return patchModule(module_under_test, "netutils") |
|
45 |
|
|
46 |
|
|
47 |
class HostnameMock(object): |
|
48 |
"""Simple mocked version of L{netutils.Hostname}. |
|
49 |
|
|
50 |
""" |
|
51 |
def __init__(self, name, ip): |
|
52 |
self.name = name |
|
53 |
self.ip = ip |
|
54 |
|
|
55 |
|
|
56 |
def _IsOverwrittenReturnValue(value): |
|
57 |
return value is not None and value != mock.DEFAULT and \ |
|
58 |
not isinstance(value, mock.Mock) |
|
59 |
|
|
60 |
|
|
61 |
# pylint: disable=W0613 |
|
62 |
def _GetHostnameMock(cfg, mock_fct, name=None, family=None): |
|
63 |
if _IsOverwrittenReturnValue(mock_fct.return_value): |
|
64 |
return mock.DEFAULT |
|
65 |
|
|
66 |
if name is None: |
|
67 |
name = cfg.GetMasterNodeName() |
|
68 |
|
|
69 |
if name == cfg.GetClusterName(): |
|
70 |
cluster = cfg.GetClusterInfo() |
|
71 |
return HostnameMock(cluster.cluster_name, cluster.master_ip) |
|
72 |
|
|
73 |
node = cfg.GetNodeInfoByName(name) |
|
74 |
if node is None: |
|
75 |
|
|
76 |
raise errors.OpPrereqError( |
|
77 |
"Mock error: The given name '%s' is not in the config" % name, |
|
78 |
errors.ECODE_RESOLVER) |
|
79 |
|
|
80 |
return HostnameMock(node.name, node.primary_ip) |
|
81 |
|
|
82 |
|
|
83 |
# pylint: disable=W0613 |
|
84 |
def _TcpPingMock(cfg, mock_fct, target, port, timeout=None, |
|
85 |
live_port_needed=None, source=None): |
|
86 |
if _IsOverwrittenReturnValue(mock_fct.return_value): |
|
87 |
return mock.DEFAULT |
|
88 |
|
|
89 |
if target == cfg.GetClusterName(): |
|
90 |
return True |
|
91 |
if cfg.GetNodeInfoByName(target) is not None: |
|
92 |
return True |
|
93 |
if target in [node.primary_ip for node in cfg.GetAllNodesInfo().values()]: |
|
94 |
return True |
|
95 |
if target in [node.secondary_ip for node in cfg.GetAllNodesInfo().values()]: |
|
96 |
return True |
|
97 |
return False |
|
98 |
|
|
99 |
|
|
100 |
def SetupDefaultNetutilsMock(netutils_mod, cfg): |
|
101 |
"""Configures the given netutils_mod mock to work with the given config. |
|
102 |
|
|
103 |
All relevant functions in netutils_mod are stubbed in such a way that they |
|
104 |
are consistent with the configuration. |
|
105 |
|
|
106 |
@param netutils_mod: the mock module to configure |
|
107 |
@type cfg: cmdlib.testsupport.ConfigMock |
|
108 |
@param cfg: the configuration to query for netutils request |
|
109 |
|
|
110 |
""" |
|
111 |
netutils_mod.GetHostname.side_effect = \ |
|
112 |
compat.partial(_GetHostnameMock, cfg, netutils_mod.GetHostname) |
|
113 |
netutils_mod.TcpPing.side_effect = \ |
|
114 |
compat.partial(_TcpPingMock, cfg, netutils_mod.TcpPing) |
|
115 |
netutils_mod.GetDaemonPort.side_effect = netutils.GetDaemonPort |
|
116 |
netutils_mod.FormatAddress.side_effect = netutils.FormatAddress |
b/test/py/cmdlib/testsupport/ssh_mock.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2013 Google Inc. |
|
5 |
# |
|
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. |
|
10 |
# |
|
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. |
|
15 |
# |
|
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 |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Support for mocking the ssh module""" |
|
23 |
|
|
24 |
|
|
25 |
from cmdlib.testsupport.util import patchModule |
|
26 |
|
|
27 |
|
|
28 |
# pylint: disable=C0103 |
|
29 |
def patchSsh(module_under_test): |
|
30 |
"""Patches the L{ganeti.ssh} module for tests. |
|
31 |
|
|
32 |
This function is meant to be used as a decorator for test methods. |
|
33 |
|
|
34 |
@type module_under_test: string |
|
35 |
@param module_under_test: the module within cmdlib which is tested. The |
|
36 |
"ganeti.cmdlib" prefix is optional. |
|
37 |
|
|
38 |
""" |
|
39 |
return patchModule(module_under_test, "ssh") |
b/test/py/cmdlib/testsupport/util.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2013 Google Inc. |
|
5 |
# |
|
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. |
|
10 |
# |
|
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. |
|
15 |
# |
|
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 |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Utility functions or the cmdlib test framework""" |
|
23 |
|
|
24 |
|
|
25 |
import mock |
|
26 |
|
|
27 |
|
|
28 |
# pylint: disable=C0103 |
|
29 |
def patchModule(module_under_test, mock_module): |
|
30 |
"""Computes the module prefix required to mock parts of the Ganeti code. |
|
31 |
|
|
32 |
@type module_under_test: string |
|
33 |
@param module_under_test: the module within cmdlib which is tested. The |
|
34 |
"ganeti.cmdlib" prefix is optional. |
|
35 |
@type mock_module |
|
36 |
@param mock_module: the module which should be mocked. |
|
37 |
|
|
38 |
""" |
|
39 |
if not module_under_test.startswith("ganeti.cmdlib"): |
|
40 |
module_under_test = "ganeti.cmdlib." + module_under_test |
|
41 |
return mock.patch("%s.%s" % (module_under_test, mock_module)) |
Also available in: Unified diff