Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / cluster_unittest.py @ bd6fb93b

History | View | Annotate | Download (13 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2008, 2011, 2012, 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 LUCluster*
23

24
"""
25

    
26
import unittest
27
import operator
28
import os
29
import tempfile
30
import shutil
31

    
32
from ganeti import constants
33
from ganeti import compat
34
from ganeti import ht
35
from ganeti import objects
36
from ganeti import opcodes
37
from ganeti import utils
38
from ganeti import pathutils
39
from ganeti import rpc
40
from ganeti import query
41
from ganeti.cmdlib import cluster
42
from ganeti.hypervisor import hv_xen
43

    
44
from testsupport import *
45

    
46
import testutils
47
import mocks
48

    
49

    
50
class TestCertVerification(testutils.GanetiTestCase):
51
  def setUp(self):
52
    testutils.GanetiTestCase.setUp(self)
53

    
54
    self.tmpdir = tempfile.mkdtemp()
55

    
56
  def tearDown(self):
57
    shutil.rmtree(self.tmpdir)
58

    
59
  def testVerifyCertificate(self):
60
    cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
61

    
62
    nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
63

    
64
    (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
65
    self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
66

    
67
    # Try to load non-certificate file
68
    invalid_cert = testutils.TestDataFilename("bdev-net.txt")
69
    (errcode, msg) = cluster._VerifyCertificate(invalid_cert)
70
    self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
71

    
72

    
73
class TestClusterVerifySsh(unittest.TestCase):
74
  def testMultipleGroups(self):
75
    fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
76
    mygroupnodes = [
77
      objects.Node(name="node20", group="my", offline=False),
78
      objects.Node(name="node21", group="my", offline=False),
79
      objects.Node(name="node22", group="my", offline=False),
80
      objects.Node(name="node23", group="my", offline=False),
81
      objects.Node(name="node24", group="my", offline=False),
82
      objects.Node(name="node25", group="my", offline=False),
83
      objects.Node(name="node26", group="my", offline=True),
84
      ]
85
    nodes = [
86
      objects.Node(name="node1", group="g1", offline=True),
87
      objects.Node(name="node2", group="g1", offline=False),
88
      objects.Node(name="node3", group="g1", offline=False),
89
      objects.Node(name="node4", group="g1", offline=True),
90
      objects.Node(name="node5", group="g1", offline=False),
91
      objects.Node(name="node10", group="xyz", offline=False),
92
      objects.Node(name="node11", group="xyz", offline=False),
93
      objects.Node(name="node40", group="alloff", offline=True),
94
      objects.Node(name="node41", group="alloff", offline=True),
95
      objects.Node(name="node50", group="aaa", offline=False),
96
      ] + mygroupnodes
97
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
98

    
99
    (online, perhost) = fn(mygroupnodes, "my", nodes)
100
    self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
101
    self.assertEqual(set(perhost.keys()), set(online))
102

    
103
    self.assertEqual(perhost, {
104
      "node20": ["node10", "node2", "node50"],
105
      "node21": ["node11", "node3", "node50"],
106
      "node22": ["node10", "node5", "node50"],
107
      "node23": ["node11", "node2", "node50"],
108
      "node24": ["node10", "node3", "node50"],
109
      "node25": ["node11", "node5", "node50"],
110
      })
111

    
112
  def testSingleGroup(self):
113
    fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
114
    nodes = [
115
      objects.Node(name="node1", group="default", offline=True),
116
      objects.Node(name="node2", group="default", offline=False),
117
      objects.Node(name="node3", group="default", offline=False),
118
      objects.Node(name="node4", group="default", offline=True),
119
      ]
120
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
121

    
122
    (online, perhost) = fn(nodes, "default", nodes)
123
    self.assertEqual(online, ["node2", "node3"])
124
    self.assertEqual(set(perhost.keys()), set(online))
125

    
126
    self.assertEqual(perhost, {
127
      "node2": [],
128
      "node3": [],
129
      })
130

    
131

    
132
class TestClusterVerifyFiles(unittest.TestCase):
133
  @staticmethod
134
  def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
135
    assert ((ecode == constants.CV_ENODEFILECHECK and
136
             ht.TNonEmptyString(item)) or
137
            (ecode == constants.CV_ECLUSTERFILECHECK and
138
             item is None))
139

    
140
    if args:
141
      msg = msg % args
142

    
143
    if cond:
144
      errors.append((item, msg))
145

    
146
  def test(self):
147
    errors = []
148
    nodeinfo = [
149
      objects.Node(name="master.example.com",
150
                   uuid="master-uuid",
151
                   offline=False,
152
                   vm_capable=True),
153
      objects.Node(name="node2.example.com",
154
                   uuid="node2-uuid",
155
                   offline=False,
156
                   vm_capable=True),
157
      objects.Node(name="node3.example.com",
158
                   uuid="node3-uuid",
159
                   master_candidate=True,
160
                   vm_capable=False),
161
      objects.Node(name="node4.example.com",
162
                   uuid="node4-uuid",
163
                   offline=False,
164
                   vm_capable=True),
165
      objects.Node(name="nodata.example.com",
166
                   uuid="nodata-uuid",
167
                   offline=False,
168
                   vm_capable=True),
169
      objects.Node(name="offline.example.com",
170
                   uuid="offline-uuid",
171
                   offline=True),
172
      ]
173
    files_all = set([
174
      pathutils.CLUSTER_DOMAIN_SECRET_FILE,
175
      pathutils.RAPI_CERT_FILE,
176
      pathutils.RAPI_USERS_FILE,
177
      ])
178
    files_opt = set([
179
      pathutils.RAPI_USERS_FILE,
180
      hv_xen.XL_CONFIG_FILE,
181
      pathutils.VNC_PASSWORD_FILE,
182
      ])
183
    files_mc = set([
184
      pathutils.CLUSTER_CONF_FILE,
185
      ])
186
    files_vm = set([
187
      hv_xen.XEND_CONFIG_FILE,
188
      hv_xen.XL_CONFIG_FILE,
189
      pathutils.VNC_PASSWORD_FILE,
190
      ])
191
    nvinfo = {
192
      "master-uuid": rpc.RpcResult(data=(True, {
193
        constants.NV_FILELIST: {
194
          pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
195
          pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
196
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
197
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
198
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
199
        }})),
200
      "node2-uuid": rpc.RpcResult(data=(True, {
201
        constants.NV_FILELIST: {
202
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
203
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
204
          }
205
        })),
206
      "node3-uuid": rpc.RpcResult(data=(True, {
207
        constants.NV_FILELIST: {
208
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
209
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
210
          }
211
        })),
212
      "node4-uuid": rpc.RpcResult(data=(True, {
213
        constants.NV_FILELIST: {
214
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
215
          pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
216
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
217
          pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
218
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
219
          }
220
        })),
221
      "nodata-uuid": rpc.RpcResult(data=(True, {})),
222
      "offline-uuid": rpc.RpcResult(offline=True),
223
      }
224
    assert set(nvinfo.keys()) == set(map(operator.attrgetter("uuid"), nodeinfo))
225

    
226
    verify_lu = cluster.LUClusterVerifyGroup(mocks.FakeProc(),
227
                                             opcodes.OpClusterVerify(),
228
                                             mocks.FakeContext(),
229
                                             None)
230

    
231
    verify_lu._ErrorIf = compat.partial(self._FakeErrorIf, errors)
232

    
233
    # TODO: That's a bit hackish to mock only this single method. We should
234
    # build a better FakeConfig which provides such a feature already.
235
    def GetNodeName(node_uuid):
236
      for node in nodeinfo:
237
        if node.uuid == node_uuid:
238
          return node.name
239
      return None
240

    
241
    verify_lu.cfg.GetNodeName = GetNodeName
242

    
243
    verify_lu._VerifyFiles(nodeinfo, "master-uuid", nvinfo,
244
                           (files_all, files_opt, files_mc, files_vm))
245
    self.assertEqual(sorted(errors), sorted([
246
      (None, ("File %s found with 2 different checksums (variant 1 on"
247
              " node2.example.com, node3.example.com, node4.example.com;"
248
              " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
249
      (None, ("File %s is missing from node(s) node2.example.com" %
250
              pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
251
      (None, ("File %s should not exist on node(s) node4.example.com" %
252
              pathutils.CLUSTER_CONF_FILE)),
253
      (None, ("File %s is missing from node(s) node4.example.com" %
254
              hv_xen.XEND_CONFIG_FILE)),
255
      (None, ("File %s is missing from node(s) node3.example.com" %
256
              pathutils.CLUSTER_CONF_FILE)),
257
      (None, ("File %s found with 2 different checksums (variant 1 on"
258
              " master.example.com; variant 2 on node4.example.com)" %
259
              pathutils.CLUSTER_CONF_FILE)),
260
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
261
              " found on master.example.com, node2.example.com,"
262
              " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
263
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
264
              " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
265
      ("nodata.example.com", "Node did not return file checksum data"),
266
      ]))
267

    
268

    
269
class TestLUClusterActivateMasterIp(CmdlibTestCase):
270
  def testSuccess(self):
271
    op = opcodes.OpClusterActivateMasterIp()
272

    
273
    self.rpc.call_node_activate_master_ip.return_value = \
274
      RpcResultsBuilder(cfg=self.cfg) \
275
        .CreateSuccessfulNodeResult(self.cfg.GetMasterNode())
276

    
277
    self.ExecOpCode(op)
278

    
279
    self.rpc.call_node_activate_master_ip.assert_called_once_with(
280
      self.cfg.GetMasterNode(),
281
      self.cfg.GetMasterNetworkParameters(),
282
      False)
283

    
284
  def testFailure(self):
285
    op = opcodes.OpClusterActivateMasterIp()
286

    
287
    self.rpc.call_node_activate_master_ip.return_value = \
288
      RpcResultsBuilder(cfg=self.cfg) \
289
        .CreateFailedNodeResult(self.cfg.GetMasterNode()) \
290

    
291
    self.ExecOpCodeExpectOpExecError(op)
292

    
293

    
294
class TestLUClusterDeactivateMasterIp(CmdlibTestCase):
295
  def testSuccess(self):
296
    op = opcodes.OpClusterDeactivateMasterIp()
297

    
298
    self.rpc.call_node_deactivate_master_ip.return_value = \
299
      RpcResultsBuilder(cfg=self.cfg) \
300
        .CreateSuccessfulNodeResult(self.cfg.GetMasterNode())
301

    
302
    self.ExecOpCode(op)
303

    
304
    self.rpc.call_node_deactivate_master_ip.assert_called_once_with(
305
      self.cfg.GetMasterNode(),
306
      self.cfg.GetMasterNetworkParameters(),
307
      False)
308

    
309
  def testFailure(self):
310
    op = opcodes.OpClusterDeactivateMasterIp()
311

    
312
    self.rpc.call_node_deactivate_master_ip.return_value = \
313
      RpcResultsBuilder(cfg=self.cfg) \
314
        .CreateFailedNodeResult(self.cfg.GetMasterNode()) \
315

    
316
    self.ExecOpCodeExpectOpExecError(op)
317

    
318

    
319
class TestLUClusterConfigQuery(CmdlibTestCase):
320
  def testInvalidField(self):
321
    op = opcodes.OpClusterConfigQuery(output_fields=["pinky_bunny"])
322

    
323
    self.ExecOpCodeExpectOpPrereqError(op, "pinky_bunny")
324

    
325
  def testAllFields(self):
326
    op = opcodes.OpClusterConfigQuery(output_fields=query.CLUSTER_FIELDS.keys())
327

    
328
    self.rpc.call_get_watcher_pause.return_value = \
329
      RpcResultsBuilder(self.cfg) \
330
        .CreateSuccessfulNodeResult(self.cfg.GetMasterNode(), -1)
331

    
332
    ret = self.ExecOpCode(op)
333

    
334
    self.assertEqual(1, self.rpc.call_get_watcher_pause.call_count)
335
    self.assertEqual(len(ret), len(query.CLUSTER_FIELDS))
336

    
337
  def testEmpytFields(self):
338
    op = opcodes.OpClusterConfigQuery(output_fields=[])
339

    
340
    self.ExecOpCode(op)
341

    
342
    self.assertFalse(self.rpc.call_get_watcher_pause.called)
343

    
344

    
345
class TestLUClusterDestroy(CmdlibTestCase):
346
  def testExistingNodes(self):
347
    op = opcodes.OpClusterDestroy()
348

    
349
    self.cfg.AddNewNode()
350
    self.cfg.AddNewNode()
351

    
352
    self.ExecOpCodeExpectOpPrereqError(op, "still 2 node\(s\)")
353

    
354
  def testExistingInstances(self):
355
    op = opcodes.OpClusterDestroy()
356

    
357
    self.cfg.AddNewInstance()
358
    self.cfg.AddNewInstance()
359

    
360
    self.ExecOpCodeExpectOpPrereqError(op, "still 2 instance\(s\)")
361

    
362
  def testEmptyCluster(self):
363
    op = opcodes.OpClusterDestroy()
364

    
365
    self.ExecOpCode(op)
366

    
367
    self.assertEqual(1, self.rpc.call_hooks_runner.call_count)
368
    args = self.rpc.call_hooks_runner.call_args[0]
369
    self.assertEqual([self.cfg.GetMasterNodeName()], args[0])
370
    self.assertEqual("cluster-destroy", args[1])
371
    self.assertEqual(constants.HOOKS_PHASE_POST, args[2])
372

    
373

    
374
if __name__ == "__main__":
375
  testutils.GanetiTestProgram()