Revision 3efa7659

b/Makefile.am
137 137
	test/data/ovfdata \
138 138
	test/data/ovfdata/other \
139 139
	test/py \
140
	test/py/cmdlib \
141
	test/py/cmdlib/testsupport \
140 142
	tools
141 143

  
142 144
ALL_APIDOC_HS_DIRS = \
......
1026 1028
	doc/examples/gnt-debug/README \
1027 1029
	doc/examples/gnt-debug/delay0.json \
1028 1030
	doc/examples/gnt-debug/delay50.json \
1029
	test/py/lockperf.py \
1030
	test/py/testutils.py \
1031
	test/py/mocks.py \
1032 1031
	$(dist_TESTS) \
1033 1032
	$(TEST_FILES) \
1033
	$(python_test_support) \
1034 1034
	man/footer.rst \
1035 1035
	$(manrst) \
1036 1036
	$(maninput) \
......
1237 1237

  
1238 1238
python_tests = \
1239 1239
	doc/examples/rapi_testutils.py \
1240
	test/py/cmdlib/test_unittest.py \
1240 1241
	test/py/cfgupgrade_unittest.py \
1241 1242
	test/py/docs_unittest.py \
1242 1243
	test/py/ganeti.asyncnotifier_unittest.py \
......
1322 1323
	test/py/qa.qa_config_unittest.py \
1323 1324
	test/py/tempfile_fork_unittest.py
1324 1325

  
1326
python_test_support = \
1327
	test/py/__init__.py \
1328
	test/py/lockperf.py \
1329
	test/py/testutils.py \
1330
	test/py/mocks.py \
1331
	test/py/cmdlib/__init__.py \
1332
	test/py/cmdlib/testsupport/__init__.py \
1333
	test/py/cmdlib/testsupport/cmdlib_testcase.py \
1334
	test/py/cmdlib/testsupport/config_mock.py \
1335
	test/py/cmdlib/testsupport/iallocator_mock.py \
1336
	test/py/cmdlib/testsupport/lock_manager_mock.py \
1337
	test/py/cmdlib/testsupport/processor_mock.py \
1338
	test/py/cmdlib/testsupport/rpc_runner_mock.py
1339

  
1325 1340
haskell_tests = test/hs/htest
1326 1341

  
1327 1342
dist_TESTS = \
......
1352 1367

  
1353 1368
# Environment for all tests
1354 1369
PLAIN_TESTS_ENVIRONMENT = \
1355
	PYTHONPATH=. \
1370
	PYTHONPATH=.:./test/py \
1356 1371
	TOP_SRCDIR=$(abs_top_srcdir) TOP_BUILDDIR=$(abs_top_builddir) \
1357 1372
	PYTHON=$(PYTHON) FAKEROOT=$(FAKEROOT_PATH) \
1358 1373
	$(RUN_IN_TEMPDIR)
......
1386 1401

  
1387 1402
if PY_UNIT
1388 1403
all_python_code += $(python_tests)
1404
all_python_code += $(python_test_support)
1389 1405
endif
1390 1406

  
1391 1407
srclink_files = \
b/lib/cmdlib/test.py
66 66

  
67 67
    """
68 68
    if self.op.on_master:
69
      if not utils.TestDelay(self.op.duration):
69
      if not utils.TestDelay(self.op.duration)[0]:
70 70
        raise errors.OpExecError("Error during master delay test")
71 71
    if self.op.on_node_uuids:
72 72
      result = self.rpc.call_test_delay(self.op.on_node_uuids, self.op.duration)
b/lib/config.py
167 167
  return result
168 168

  
169 169

  
170
class ConfigWriter:
170
class ConfigWriter(object):
171 171
  """The interface to the cluster configuration.
172 172

  
173 173
  @ivar _temporary_lvs: reservation manager for temporary LVs
b/lib/locking.py
1625 1625
NAL = "NAL"
1626 1626

  
1627 1627

  
1628
class GanetiLockManager:
1628
class GanetiLockManager(object):
1629 1629
  """The Ganeti Locking Library
1630 1630

  
1631 1631
  The purpose of this small library is to manage locking for ganeti clusters
b/test/py/__init__.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
"""This module contains all python test code"""
b/test/py/cmdlib/__init__.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
"""This module contains all cmdlib unit tests"""
b/test/py/cmdlib/test_unittest.py
1
#!/usr/bin/python
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
"""Tests for LUTest*
23

  
24
"""
25

  
26

  
27
from ganeti import constants
28
from ganeti import errors
29
from ganeti import opcodes
30

  
31
from testsupport import *
32

  
33
import testutils
34

  
35
DELAY_DURATION = 0.01
36

  
37

  
38
class TestLUTestDelay(CmdlibTestCase):
39
  def testRepeatedInvocation(self):
40
    op = opcodes.OpTestDelay(duration=DELAY_DURATION,
41
                             repeat=3)
42
    self.ExecOpCode(op)
43

  
44
    self.assertLogContainsMessage(" - INFO: Test delay iteration 0/2")
45
    self.mcpu.assertLogContainsEntry(constants.ELOG_MESSAGE,
46
                                     " - INFO: Test delay iteration 1/2")
47
    self.assertLogContainsRegex("2/2$")
48

  
49
  def testInvalidDuration(self):
50
    op = opcodes.OpTestDelay(duration=-1)
51

  
52
    self.assertRaises(errors.OpExecError, self.ExecOpCode, op)
53

  
54
  def testOnNodeUuid(self):
55
    node_uuids = [self.cfg.GetMasterNode()]
56
    op = opcodes.OpTestDelay(duration=DELAY_DURATION,
57
                             on_node_uuids=node_uuids)
58
    self.ExecOpCode(op)
59

  
60
    self.rpc.call_test_delay.assert_called_once_with(node_uuids, DELAY_DURATION)
61

  
62
  def testOnNodeName(self):
63
    op = opcodes.OpTestDelay(duration=DELAY_DURATION,
64
                             on_nodes=[self.cfg.GetMasterNodeName()])
65
    self.ExecOpCode(op)
66

  
67
    self.rpc.call_test_delay.assert_called_once_with([self.cfg.GetMasterNode()],
68
                                                     DELAY_DURATION)
69

  
70
  def testSuccessfulRpc(self):
71
    op = opcodes.OpTestDelay(duration=DELAY_DURATION,
72
                             on_nodes=[self.cfg.GetMasterNodeName()])
73

  
74
    self.rpc.call_test_delay.return_value = \
75
      RpcResultsBuilder(cfg=self.cfg) \
76
        .AddSuccessfulNode(self.cfg.GetMasterNode()) \
77
        .Build()
78

  
79
    self.ExecOpCode(op)
80

  
81
    self.rpc.call_test_delay.assert_called_once()
82

  
83
  def testFailingRpc(self):
84
    op = opcodes.OpTestDelay(duration=DELAY_DURATION,
85
                             on_nodes=[self.cfg.GetMasterNodeName()])
86

  
87
    self.rpc.call_test_delay.return_value = \
88
      RpcResultsBuilder(cfg=self.cfg) \
89
        .AddFailedNode(self.cfg.GetMasterNode()) \
90
        .Build()
91

  
92
    self.assertRaises(errors.OpExecError, self.ExecOpCode, op)
93

  
94
  def testMultipleNodes(self):
95
    node1 = self.cfg.AddNewNode()
96
    node2 = self.cfg.AddNewNode()
97
    op = opcodes.OpTestDelay(duration=DELAY_DURATION,
98
                             on_nodes=[node1.name, node2.name])
99

  
100
    self.rpc.call_test_delay.return_value = \
101
      RpcResultsBuilder(cfg=self.cfg) \
102
        .AddSuccessfulNode(node1) \
103
        .AddSuccessfulNode(node2) \
104
        .Build()
105

  
106
    self.ExecOpCode(op)
107

  
108
    self.rpc.call_test_delay.assert_called_once_with([node1.uuid, node2.uuid],
109
                                                     DELAY_DURATION)
110

  
111

  
112
if __name__ == "__main__":
113
  testutils.GanetiTestProgram()
b/test/py/cmdlib/testsupport/__init__.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 classes and functions for testing the cmdlib module.
23

  
24
"""
25

  
26
from cmdlib_testcase import CmdlibTestCase
27
from config_mock import ConfigMock
28
from iallocator_mock import CreateIAllocatorMock
29
from lock_manager_mock import LockManagerMock
30
from processor_mock import ProcessorMock
31
from rpc_runner_mock import CreateRpcRunnerMock, RpcResultsBuilder
32

  
33
__all__ = ["CmdlibTestCase",
34
           "ConfigMock",
35
           "CreateIAllocatorMock",
36
           "CreateRpcRunnerMock",
37
           "LockManagerMock",
38
           "ProcessorMock",
39
           "RpcResultsBuilder",
40
          ]
b/test/py/cmdlib/testsupport/cmdlib_testcase.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
from config_mock import *
22
from iallocator_mock import *
23
from lock_manager_mock import *
24
from processor_mock import *
25
from rpc_runner_mock import *
26

  
27
import testutils
28

  
29

  
30
class GanetiContextMock(object):
31
  def __init__(self, cfg, glm, rpc):
32
    self.cfg = cfg
33
    self.glm = glm
34
    self.rpc = rpc
35

  
36

  
37
class CmdlibTestCase(testutils.GanetiTestCase):
38
  """Base class for cmdlib tests.
39

  
40
  This class sets up a mocked environment for the execution of
41
  L{ganeti.cmdlib.base.LogicalUnit} subclasses.
42

  
43
  The environment can be customized via the following fields:
44

  
45
    * C{cfg}: @see L{ConfigMock}
46
    * C{glm}: @see L{LockManagerMock}
47
    * C{rpc}: @see L{CreateRpcRunnerMock}
48
    * C{iallocator}: @see L{CreateIAllocatorMock}
49
    * C{mcpu}: @see L{ProcessorMock}
50

  
51
  """
52
  def setUp(self):
53
    super(CmdlibTestCase, self).setUp()
54

  
55
    self.cfg = ConfigMock()
56
    self.glm = LockManagerMock()
57
    self.rpc = CreateRpcRunnerMock()
58
    self.iallocator = CreateIAllocatorMock()
59
    ctx = GanetiContextMock(self.cfg, self.glm, self.rpc)
60
    self.mcpu = ProcessorMock(ctx)
61

  
62
  def tearDown(self):
63
    super(CmdlibTestCase, self).tearDown()
64

  
65
  def ExecOpCode(self, opcode):
66
    """Executes the given opcode.
67

  
68
    @param opcode: the opcode to execute
69
    @return: the result of the LU's C{Exec} method
70
    """
71
    self.glm.AddLocksFromConfig(self.cfg)
72

  
73
    return self.mcpu.ExecOpCodeAndRecordOutput(opcode)
74

  
75
  def assertLogContainsMessage(self, expected_msg):
76
    """Shortcut for L{ProcessorMock.assertLogContainsMessage}
77

  
78
    """
79
    self.mcpu.assertLogContainsMessage(expected_msg)
80

  
81
  def assertLogContainsRegex(self, expected_regex):
82
    """Shortcut for L{ProcessorMock.assertLogContainsRegex}
83

  
84
    """
85
    self.mcpu.assertLogContainsRegex(expected_regex)
b/test/py/cmdlib/testsupport/config_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
import uuid
22

  
23
from ganeti import config
24
from ganeti import constants
25
from ganeti import objects
26

  
27
import mocks
28

  
29

  
30
def _StubGetEntResolver():
31
  return mocks.FakeGetentResolver()
32

  
33

  
34
class ConfigMock(config.ConfigWriter):
35
  """A mocked cluster configuration with added methods for easy customization.
36

  
37
  """
38

  
39
  def __init__(self):
40
    self._cur_group_id = 1
41
    self._cur_node_id = 1
42
    self._cur_inst_id = 1
43

  
44
    super(ConfigMock, self).__init__(cfg_file="/dev/null",
45
                                     _getents=_StubGetEntResolver())
46

  
47
  def _GetUuid(self):
48
    return str(uuid.uuid4())
49

  
50
  def AddNewNodeGroup(self,
51
                      uuid=None,
52
                      name=None,
53
                      ndparams=None,
54
                      diskparams=None,
55
                      ipolicy=None,
56
                      hv_state_static=None,
57
                      disk_state_static=None,
58
                      alloc_policy=None,
59
                      networks=[]):
60
    """Add a new L{objects.NodeGroup} to the cluster configuration
61

  
62
    See L{objects.NodeGroup} for parameter documentation.
63

  
64
    @rtype: L{objects.NodeGroup}
65
    @return: the newly added node group
66

  
67
    """
68
    group_id = self._cur_group_id
69
    self._cur_group_id += 1
70

  
71
    if uuid is None:
72
      uuid = self._GetUuid()
73
    if name is None:
74
      name = "mock_group_%d" % group_id
75

  
76
    group = objects.NodeGroup(uuid=uuid,
77
                              name=name,
78
                              ndparams=ndparams,
79
                              diskparams=diskparams,
80
                              ipolicy=ipolicy,
81
                              hv_state_static=hv_state_static,
82
                              disk_state_static=disk_state_static,
83
                              alloc_policy=alloc_policy,
84
                              networks=networks,
85
                              members=[])
86

  
87
    self.AddNodeGroup(group, None)
88
    return group
89

  
90
  def AddNewNode(self,
91
                 uuid=None,
92
                 name=None,
93
                 primary_ip=None,
94
                 secondary_ip=None,
95
                 master_candidate=True,
96
                 offline=False,
97
                 drained=False,
98
                 group=None,
99
                 master_capable=True,
100
                 vm_capable=True,
101
                 ndparams=None,
102
                 powered=True,
103
                 hv_state=None,
104
                 hv_state_static=None,
105
                 disk_state=None,
106
                 disk_state_static=None):
107
    """Add a new L{objects.Node} to the cluster configuration
108

  
109
    See L{objects.Node} for parameter documentation.
110

  
111
    @rtype: L{objects.Node}
112
    @return: the newly added node
113

  
114
    """
115
    node_id = self._cur_node_id
116
    self._cur_node_id += 1
117

  
118
    if uuid is None:
119
      uuid = self._GetUuid()
120
    if name is None:
121
      name = "mock_node_%d.example.com" % node_id
122
    if primary_ip is None:
123
      primary_ip = "192.168.0.%d" % node_id
124
    if secondary_ip is None:
125
      secondary_ip = "192.168.1.%d" % node_id
126
    if group is None:
127
      group = self._default_group.uuid
128
    if isinstance(group, objects.NodeGroup):
129
      group = group.uuid
130

  
131
    node = objects.Node(uuid=uuid,
132
                        name=name,
133
                        primary_ip=primary_ip,
134
                        secondary_ip=secondary_ip,
135
                        master_candidate=master_candidate,
136
                        offline=offline,
137
                        drained=drained,
138
                        group=group,
139
                        master_capable=master_capable,
140
                        vm_capable=vm_capable,
141
                        ndparams=ndparams,
142
                        powered=powered,
143
                        hv_state=hv_state,
144
                        hv_state_static=hv_state_static,
145
                        disk_state=disk_state,
146
                        disk_state_static=disk_state_static)
147

  
148
    self.AddNode(node, None)
149
    return node
150

  
151
  def AddNewInstance(self,
152
                     uuid=None,
153
                     name=None,
154
                     primary_node=None,
155
                     os="mocked_os",
156
                     hypervisor=constants.HT_FAKE,
157
                     hvparams={},
158
                     beparams={},
159
                     osparams={},
160
                     admin_state=constants.ADMINST_DOWN,
161
                     nics=[],
162
                     disks=[],
163
                     disk_template=constants.DT_DISKLESS,
164
                     disks_active=False,
165
                     network_port=None):
166
    """Add a new L{objects.Instance} to the cluster configuration
167

  
168
    See L{objects.Instance} for parameter documentation.
169

  
170
    @rtype: L{objects.Instance}
171
    @return: the newly added instance
172

  
173
    """
174
    inst_id = self._cur_inst_id
175
    self._cur_inst_id += 1
176

  
177
    if uuid is None:
178
      uuid = self._GetUuid()
179
    if name is None:
180
      name = "mock_inst_%d.example.com" % inst_id
181
    if primary_node is None:
182
      primary_node = self._master_node.uuid
183
    if isinstance(primary_node, objects.Node):
184
      primary_node = self._master_node.uuid
185

  
186
    inst = objects.Instance(uuid=uuid,
187
                            name=name,
188
                            primary_node=primary_node,
189
                            os=os,
190
                            hypervisor=hypervisor,
191
                            hvparams=hvparams,
192
                            beparams=beparams,
193
                            osparams=osparams,
194
                            admin_state=admin_state,
195
                            nics=nics,
196
                            disks=disks,
197
                            disk_template=disk_template,
198
                            disks_active=disks_active,
199
                            network_port=network_port)
200
    self.AddInstance(inst, None)
201
    return inst
202

  
203
  def _OpenConfig(self, accept_foreign):
204
    self._config_data = objects.ConfigData(
205
      version=constants.CONFIG_VERSION,
206
      cluster=None,
207
      nodegroups={},
208
      nodes={},
209
      instances={},
210
      networks={})
211

  
212
    master_node_uuid = self._GetUuid()
213

  
214
    self._cluster = objects.Cluster(
215
      serial_no=1,
216
      rsahostkeypub="",
217
      highest_used_port=(constants.FIRST_DRBD_PORT - 1),
218
      mac_prefix="aa:00:00",
219
      volume_group_name="xenvg",
220
      drbd_usermode_helper="/bin/true",
221
      nicparams={constants.PP_DEFAULT: constants.NICC_DEFAULTS},
222
      ndparams=constants.NDC_DEFAULTS,
223
      tcpudp_port_pool=set(),
224
      enabled_hypervisors=[constants.HT_FAKE],
225
      master_node=master_node_uuid,
226
      master_ip="192.168.0.254",
227
      master_netdev=constants.DEFAULT_BRIDGE,
228
      cluster_name="cluster.example.com",
229
      file_storage_dir="/tmp",
230
      uid_pool=[],
231
      )
232
    self._config_data.cluster = self._cluster
233

  
234
    self._default_group = self.AddNewNodeGroup(name="default")
235
    self._master_node = self.AddNewNode(uuid=master_node_uuid)
236

  
237
  def _WriteConfig(self, destination=None, feedback_fn=None):
238
    pass
239

  
240
  def _DistributeConfig(self, feedback_fn):
241
    pass
242

  
243
  def _GetRpc(self, address_list):
244
    raise NotImplementedError
b/test/py/cmdlib/testsupport/iallocator_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
import mock
22

  
23
from ganeti.masterd import iallocator
24

  
25

  
26
def CreateIAllocatorMock():
27
  """Creates a new L{mock.MagicMock} tailored for L{iallocator.IAllocator}
28

  
29
  """
30
  ret = mock.MagicMock(spec=iallocator.IAllocator)
31
  return ret
b/test/py/cmdlib/testsupport/lock_manager_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
from ganeti import locking
22

  
23

  
24
class LockManagerMock(locking.GanetiLockManager):
25
  """Mocked lock manager for tests.
26

  
27
  """
28
  def __init__(self):
29
    # reset singleton instance, there is a separate lock manager for every test
30
    self.__class__._instance = None
31

  
32
    super(LockManagerMock, self).__init__([], [], [], [])
33

  
34
  def AddLocksFromConfig(self, cfg):
35
    """Create locks for all entities in the given configuration.
36

  
37
    @type cfg: ganeti.config.ConfigWriter
38
    """
39
    try:
40
      self.acquire(locking.LEVEL_CLUSTER, locking.BGL)
41

  
42
      for node_uuid in cfg.GetNodeList():
43
        self.add(locking.LEVEL_NODE, node_uuid)
44
        self.add(locking.LEVEL_NODE_RES, node_uuid)
45
      for group_uuid in cfg.GetNodeGroupList():
46
        self.add(locking.LEVEL_NODEGROUP, group_uuid)
47
      for inst in cfg.GetAllInstancesInfo().values():
48
        self.add(locking.LEVEL_INSTANCE, inst.name)
49
      for net_uuid in cfg.GetNetworkList():
50
        self.add(locking.LEVEL_NETWORK, net_uuid)
51
    finally:
52
      self.release(locking.LEVEL_CLUSTER, locking.BGL)
b/test/py/cmdlib/testsupport/processor_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
import re
22

  
23
from ganeti import constants
24
from ganeti import mcpu
25

  
26

  
27
class LogRecordingCallback(mcpu.OpExecCbBase):
28
  """Helper class for log output recording.
29

  
30
  """
31
  def __init__(self, processor):
32
    self.processor = processor
33

  
34
  def Feedback(self, *args):
35
    assert len(args) < 3
36

  
37
    if len(args) == 1:
38
      log_type = constants.ELOG_MESSAGE
39
      log_msg = args[0]
40
    else:
41
      (log_type, log_msg) = args
42

  
43
    self.processor.log_entries.append((log_type, log_msg))
44

  
45

  
46
class ProcessorMock(mcpu.Processor):
47
  """Mocked opcode processor for tests.
48

  
49
  This class actually performs much more than a mock, as it drives the
50
  execution of LU's. But it also provides access to the log output of the LU
51
  the result of the execution.
52

  
53
  See L{ExecOpCodeAndRecordOutput} for the main method of this class.
54

  
55
  """
56

  
57
  def __init__(self, context):
58
    super(ProcessorMock, self).__init__(context, 0, True)
59
    self.log_entries = []
60

  
61
  def ExecOpCodeAndRecordOutput(self, op):
62
    """Executes the given opcode and records the output for further inspection.
63

  
64
    @param op: the opcode to execute.
65
    @return: see L{mcpu.Processor.ExecOpCode}
66

  
67
    """
68
    return self.ExecOpCode(op, LogRecordingCallback(self))
69

  
70
  def GetLogEntries(self):
71
    """Return the list of recorded log entries.
72

  
73
    @rtype: list of (string, string) tuples
74
    @return: the list of recorded log entries
75

  
76
    """
77
    return self.log_entries
78

  
79
  def GetLogMessages(self):
80
    """Return the list of recorded log messages.
81

  
82
    @rtype: list of string
83
    @return: the list of recorded log messages
84

  
85
    """
86
    return [msg for _, msg in self.log_entries]
87

  
88
  def GetLogEntriesString(self):
89
    """Return a string with all log entries separated by a newline.
90

  
91
    """
92
    return "\n".join("%s: %s" % (type, msg)
93
                     for type, msg in self.GetLogEntries())
94

  
95
  def GetLogMessagesString(self):
96
    """Return a string with all log messages separated by a newline.
97

  
98
    """
99
    return "\n".join("%s" % msg for _, msg in self.GetLogEntries())
100

  
101
  def assertLogContainsEntry(self, expected_type, expected_msg):
102
    """Asserts that the log contains the exact given entry.
103

  
104
    @type expected_type: string
105
    @param expected_type: the expected type
106
    @type expected_msg: string
107
    @param expected_msg: the expected message
108

  
109
    """
110
    for type, msg in self.log_entries:
111
      if type == expected_type and msg == expected_msg:
112
        return
113

  
114
    raise AssertionError(
115
      "Could not find '%s' (type '%s') in LU log messages. Log is:\n%s" %
116
      (expected_msg, expected_type, self.GetLogEntriesString()))
117

  
118
  def assertLogContainsMessage(self, expected_msg):
119
    """Asserts that the log contains the exact given message.
120

  
121
    @type expected_msg: string
122
    @param expected_msg: the expected message
123

  
124
    """
125
    for msg in self.GetLogMessages():
126
      if msg == expected_msg:
127
        return
128

  
129
    raise AssertionError(
130
      "Could not find '%s' in LU log messages. Log is:\n%s" %
131
      (expected_msg, self.GetLogMessagesString()))
132

  
133
  def assertLogContainsRegex(self, expected_regex):
134
    """Asserts that the log contains a message which matches the regex.
135

  
136
    @type expected_regex: string
137
    @param expected_regex: regular expression to match messages with.
138

  
139
    """
140
    for msg in self.GetLogMessages():
141
      if re.search(expected_regex, msg) is not None:
142
        return
143

  
144
    raise AssertionError(
145
      "Could not find '%s' in LU log messages. Log is:\n%s" %
146
      (expected_regex, self.GetLogMessagesString())
147
    )
b/test/py/cmdlib/testsupport/rpc_runner_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
import mock
22

  
23
from ganeti import objects
24
from ganeti import rpc
25

  
26

  
27
def CreateRpcRunnerMock():
28
  """Creates a new L{mock.MagicMock} tailored for L{rpc.RpcRunner}
29

  
30
  """
31
  ret = mock.MagicMock(spec=rpc.RpcRunner)
32
  return ret
33

  
34

  
35
class RpcResultsBuilder(object):
36
  """Helper class which assists in constructing L{rpc.RpcResult} objects.
37

  
38
  This class provides some convenience methods for constructing L{rpc.RpcResult}
39
  objects. It is possible to create single results with the C{Create*} methods
40
  or to create multi-node results by repeatedly calling the C{Add*} methods and
41
  then obtaining the final result with C{Build}.
42

  
43
  The C{node} parameter of all the methods can either be a L{objects.Node}
44
  object, a node UUID or a node name. You have to provide the cluster config
45
  in the constructor if you want to use node UUID's/names.
46

  
47
  A typical usage of this class is as follows::
48

  
49
    self.rpc.call_some_rpc.return_value = \
50
      RpcResultsBuilder(cfg=self.cfg) \
51
        .AddSuccessfulNode(node1,
52
                           {
53
                             "result_key": "result_data",
54
                             "another_key": "other_data",
55
                           }) \
56
        .AddErrorNode(node2) \
57
        .Build()
58

  
59
  """
60

  
61
  def __init__(self, cfg=None, use_node_names=False):
62
    """Constructor.
63

  
64
    @type cfg: L{ganeti.config.ConfigWriter}
65
    @param cfg: used to resolve nodes if not C{None}
66
    @type use_node_names: bool
67
    @param use_node_names: if set to C{True}, the node field in the RPC results
68
          will contain the node name instead of the node UUID.
69
    """
70
    self._cfg = cfg
71
    self._use_node_names = use_node_names
72
    self._results = []
73

  
74
  def _GetNode(self, node_id):
75
    if isinstance(node_id, objects.Node):
76
      return node_id
77

  
78
    node = None
79
    if self._cfg is not None:
80
      node = self._cfg.GetNodeInfo(node_id)
81
      if node is None:
82
        node = self._cfg.GetNodeInfoByName(node_id)
83

  
84
    assert node is not None, "Failed to find '%s' in configuration" % node_id
85
    return node
86

  
87
  def _GetNodeId(self, node_id):
88
    node = self._GetNode(node_id)
89
    if self._use_node_names:
90
      return node.name
91
    else:
92
      return node.uuid
93

  
94
  def CreateSuccessfulNodeResult(self, node, data={}):
95
    """@see L{RpcResultsBuilder}
96

  
97
    @param node: @see L{RpcResultsBuilder}.
98
    @type data: dict
99
    @param data: the data as returned by the RPC
100
    @rtype: L{rpc.RpcResult}
101
    """
102
    return rpc.RpcResult(data=(True, data), node=self._GetNodeId(node))
103

  
104
  def CreateFailedNodeResult(self, node):
105
    """@see L{RpcResultsBuilder}
106

  
107
    @param node: @see L{RpcResultsBuilder}.
108
    @rtype: L{rpc.RpcResult}
109
    """
110
    return rpc.RpcResult(failed=True, node=self._GetNodeId(node))
111

  
112
  def CreateOfflineNodeResult(self, node):
113
    """@see L{RpcResultsBuilder}
114

  
115
    @param node: @see L{RpcResultsBuilder}.
116
    @rtype: L{rpc.RpcResult}
117
    """
118
    return rpc.RpcResult(failed=True, node=self._GetNodeId(node))
119

  
120
  def CreateErrorNodeResult(self, node, error_msg=None):
121
    """@see L{RpcResultsBuilder}
122

  
123
    @param node: @see L{RpcResultsBuilder}.
124
    @type error_msg: string
125
    @param error_msg: the error message as returned by the RPC
126
    @rtype: L{rpc.RpcResult}
127
    """
128
    return rpc.RpcResult(data=(False, error_msg), node=self._GetNodeId(node))
129

  
130
  def AddSuccessfulNode(self, node, data={}):
131
    """@see L{CreateSuccessfulNode}"""
132
    self._results.append(self.CreateSuccessfulNodeResult(node, data))
133
    return self
134

  
135
  def AddFailedNode(self, node):
136
    """@see L{CreateFailedNode}"""
137
    self._results.append(self.CreateFailedNodeResult(node))
138
    return self
139

  
140
  def AddOfflineNode(self, node):
141
    """@see L{CreateOfflineNode}"""
142
    self._results.append(self.CreateOfflineNodeResult(node))
143

  
144
  def AddErrorNode(self, node, error_msg=None):
145
    """@see L{CreateErrorNode}"""
146
    self._results.append(self.CreateErrorNodeResult(node, error_msg=error_msg))
147

  
148
  def Build(self):
149
    """Creates a dictionary holding multi-node results
150

  
151
    @rtype: dict
152
    """
153
    return dict((result.node, result) for result in self._results)

Also available in: Unified diff