Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / cluster_unittest.py @ 916c0e6f

History | View | Annotate | Download (74.4 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 OpenSSL
27

    
28
import unittest
29
import operator
30
import os
31
import tempfile
32
import shutil
33

    
34
from ganeti import constants
35
from ganeti import errors
36
from ganeti import netutils
37
from ganeti import objects
38
from ganeti import opcodes
39
from ganeti import utils
40
from ganeti import pathutils
41
from ganeti import query
42
from ganeti.cmdlib import cluster
43
from ganeti.hypervisor import hv_xen
44

    
45
from testsupport import *
46

    
47
import testutils
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 TestLUClusterActivateMasterIp(CmdlibTestCase):
133
  def testSuccess(self):
134
    op = opcodes.OpClusterActivateMasterIp()
135

    
136
    self.rpc.call_node_activate_master_ip.return_value = \
137
      self.RpcResultsBuilder() \
138
        .CreateSuccessfulNodeResult(self.master)
139

    
140
    self.ExecOpCode(op)
141

    
142
    self.rpc.call_node_activate_master_ip.assert_called_once_with(
143
      self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
144

    
145
  def testFailure(self):
146
    op = opcodes.OpClusterActivateMasterIp()
147

    
148
    self.rpc.call_node_activate_master_ip.return_value = \
149
      self.RpcResultsBuilder() \
150
        .CreateFailedNodeResult(self.master) \
151

    
152
    self.ExecOpCodeExpectOpExecError(op)
153

    
154

    
155
class TestLUClusterDeactivateMasterIp(CmdlibTestCase):
156
  def testSuccess(self):
157
    op = opcodes.OpClusterDeactivateMasterIp()
158

    
159
    self.rpc.call_node_deactivate_master_ip.return_value = \
160
      self.RpcResultsBuilder() \
161
        .CreateSuccessfulNodeResult(self.master)
162

    
163
    self.ExecOpCode(op)
164

    
165
    self.rpc.call_node_deactivate_master_ip.assert_called_once_with(
166
      self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
167

    
168
  def testFailure(self):
169
    op = opcodes.OpClusterDeactivateMasterIp()
170

    
171
    self.rpc.call_node_deactivate_master_ip.return_value = \
172
      self.RpcResultsBuilder() \
173
        .CreateFailedNodeResult(self.master) \
174

    
175
    self.ExecOpCodeExpectOpExecError(op)
176

    
177

    
178
class TestLUClusterConfigQuery(CmdlibTestCase):
179
  def testInvalidField(self):
180
    op = opcodes.OpClusterConfigQuery(output_fields=["pinky_bunny"])
181

    
182
    self.ExecOpCodeExpectOpPrereqError(op, "pinky_bunny")
183

    
184
  def testAllFields(self):
185
    op = opcodes.OpClusterConfigQuery(output_fields=query.CLUSTER_FIELDS.keys())
186

    
187
    self.rpc.call_get_watcher_pause.return_value = \
188
      self.RpcResultsBuilder() \
189
        .CreateSuccessfulNodeResult(self.master, -1)
190

    
191
    ret = self.ExecOpCode(op)
192

    
193
    self.assertEqual(1, self.rpc.call_get_watcher_pause.call_count)
194
    self.assertEqual(len(ret), len(query.CLUSTER_FIELDS))
195

    
196
  def testEmpytFields(self):
197
    op = opcodes.OpClusterConfigQuery(output_fields=[])
198

    
199
    self.ExecOpCode(op)
200

    
201
    self.assertFalse(self.rpc.call_get_watcher_pause.called)
202

    
203

    
204
class TestLUClusterDestroy(CmdlibTestCase):
205
  def testExistingNodes(self):
206
    op = opcodes.OpClusterDestroy()
207

    
208
    self.cfg.AddNewNode()
209
    self.cfg.AddNewNode()
210

    
211
    self.ExecOpCodeExpectOpPrereqError(op, "still 2 node\(s\)")
212

    
213
  def testExistingInstances(self):
214
    op = opcodes.OpClusterDestroy()
215

    
216
    self.cfg.AddNewInstance()
217
    self.cfg.AddNewInstance()
218

    
219
    self.ExecOpCodeExpectOpPrereqError(op, "still 2 instance\(s\)")
220

    
221
  def testEmptyCluster(self):
222
    op = opcodes.OpClusterDestroy()
223

    
224
    self.ExecOpCode(op)
225

    
226
    self.assertSingleHooksCall([self.master.name],
227
                               "cluster-destroy",
228
                               constants.HOOKS_PHASE_POST)
229

    
230

    
231
class TestLUClusterPostInit(CmdlibTestCase):
232
  def testExecuion(self):
233
    op = opcodes.OpClusterPostInit()
234

    
235
    self.ExecOpCode(op)
236

    
237
    self.assertSingleHooksCall([self.master.name],
238
                               "cluster-init",
239
                               constants.HOOKS_PHASE_POST)
240

    
241

    
242
class TestLUClusterQuery(CmdlibTestCase):
243
  def testSimpleInvocation(self):
244
    op = opcodes.OpClusterQuery()
245

    
246
    self.ExecOpCode(op)
247

    
248
  def testIPv6Cluster(self):
249
    op = opcodes.OpClusterQuery()
250

    
251
    self.cluster.primary_ip_family = netutils.IP6Address.family
252

    
253
    self.ExecOpCode(op)
254

    
255

    
256
class TestLUClusterRedistConf(CmdlibTestCase):
257
  def testSimpleInvocation(self):
258
    op = opcodes.OpClusterRedistConf()
259

    
260
    self.ExecOpCode(op)
261

    
262

    
263
class TestLUClusterRename(CmdlibTestCase):
264
  NEW_NAME = "new-name.example.com"
265
  NEW_IP = "203.0.113.100"
266

    
267
  def testNoChanges(self):
268
    op = opcodes.OpClusterRename(name=self.cfg.GetClusterName())
269

    
270
    self.ExecOpCodeExpectOpPrereqError(op, "name nor the IP address")
271

    
272
  def testReachableIp(self):
273
    op = opcodes.OpClusterRename(name=self.NEW_NAME)
274

    
275
    self.netutils_mod.GetHostname.return_value = \
276
      HostnameMock(self.NEW_NAME, self.NEW_IP)
277
    self.netutils_mod.TcpPing.return_value = True
278

    
279
    self.ExecOpCodeExpectOpPrereqError(op, "is reachable on the network")
280

    
281
  def testValidRename(self):
282
    op = opcodes.OpClusterRename(name=self.NEW_NAME)
283

    
284
    self.netutils_mod.GetHostname.return_value = \
285
      HostnameMock(self.NEW_NAME, self.NEW_IP)
286

    
287
    self.ExecOpCode(op)
288

    
289
    self.assertEqual(1, self.ssh_mod.WriteKnownHostsFile.call_count)
290
    self.rpc.call_node_deactivate_master_ip.assert_called_once_with(
291
      self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
292
    self.rpc.call_node_activate_master_ip.assert_called_once_with(
293
      self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
294

    
295
  def testRenameOfflineMaster(self):
296
    op = opcodes.OpClusterRename(name=self.NEW_NAME)
297

    
298
    self.master.offline = True
299
    self.netutils_mod.GetHostname.return_value = \
300
      HostnameMock(self.NEW_NAME, self.NEW_IP)
301

    
302
    self.ExecOpCode(op)
303

    
304

    
305
class TestLUClusterRepairDiskSizes(CmdlibTestCase):
306
  def testNoInstances(self):
307
    op = opcodes.OpClusterRepairDiskSizes()
308

    
309
    self.ExecOpCode(op)
310

    
311
  def _SetUpInstanceSingleDisk(self, dev_type=constants.DT_PLAIN):
312
    pnode = self.master
313
    snode = self.cfg.AddNewNode()
314

    
315
    disk = self.cfg.CreateDisk(dev_type=dev_type,
316
                               primary_node=pnode,
317
                               secondary_node=snode)
318
    inst = self.cfg.AddNewInstance(disks=[disk])
319

    
320
    return (inst, disk)
321

    
322
  def testSingleInstanceOnFailingNode(self):
323
    (inst, _) = self._SetUpInstanceSingleDisk()
324
    op = opcodes.OpClusterRepairDiskSizes(instances=[inst.name])
325

    
326
    self.rpc.call_blockdev_getdimensions.return_value = \
327
      self.RpcResultsBuilder() \
328
        .CreateFailedNodeResult(self.master)
329

    
330
    self.ExecOpCode(op)
331

    
332
    self.mcpu.assertLogContainsRegex("Failure in blockdev_getdimensions")
333

    
334
  def _ExecOpClusterRepairDiskSizes(self, node_data):
335
    # not specifying instances repairs all
336
    op = opcodes.OpClusterRepairDiskSizes()
337

    
338
    self.rpc.call_blockdev_getdimensions.return_value = \
339
      self.RpcResultsBuilder() \
340
        .CreateSuccessfulNodeResult(self.master, node_data)
341

    
342
    return self.ExecOpCode(op)
343

    
344
  def testInvalidResultData(self):
345
    for data in [[], [None], ["invalid"], [("still", "invalid")]]:
346
      self.ResetMocks()
347

    
348
      self._SetUpInstanceSingleDisk()
349
      self._ExecOpClusterRepairDiskSizes(data)
350

    
351
      self.mcpu.assertLogContainsRegex("ignoring")
352

    
353
  def testCorrectSize(self):
354
    self._SetUpInstanceSingleDisk()
355
    changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
356
    self.mcpu.assertLogIsEmpty()
357
    self.assertEqual(0, len(changed))
358

    
359
  def testWrongSize(self):
360
    self._SetUpInstanceSingleDisk()
361
    changed = self._ExecOpClusterRepairDiskSizes([(512 * 1024 * 1024, None)])
362
    self.assertEqual(1, len(changed))
363

    
364
  def testCorrectDRBD(self):
365
    self._SetUpInstanceSingleDisk(dev_type=constants.DT_DRBD8)
366
    changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
367
    self.mcpu.assertLogIsEmpty()
368
    self.assertEqual(0, len(changed))
369

    
370
  def testWrongDRBDChild(self):
371
    (_, disk) = self._SetUpInstanceSingleDisk(dev_type=constants.DT_DRBD8)
372
    disk.children[0].size = 512
373
    changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
374
    self.assertEqual(1, len(changed))
375

    
376
  def testExclusiveStorageInvalidResultData(self):
377
    self._SetUpInstanceSingleDisk()
378
    self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
379
    self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
380

    
381
    self.mcpu.assertLogContainsRegex(
382
      "did not return valid spindles information")
383

    
384
  def testExclusiveStorageCorrectSpindles(self):
385
    (_, disk) = self._SetUpInstanceSingleDisk()
386
    disk.spindles = 1
387
    self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
388
    changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, 1)])
389
    self.assertEqual(0, len(changed))
390

    
391
  def testExclusiveStorageWrongSpindles(self):
392
    self._SetUpInstanceSingleDisk()
393
    self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
394
    changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, 1)])
395
    self.assertEqual(1, len(changed))
396

    
397

    
398
class TestLUClusterSetParams(CmdlibTestCase):
399
  UID_POOL = [(10, 1000)]
400

    
401
  def testUidPool(self):
402
    op = opcodes.OpClusterSetParams(uid_pool=self.UID_POOL)
403
    self.ExecOpCode(op)
404
    self.assertEqual(self.UID_POOL, self.cluster.uid_pool)
405

    
406
  def testAddUids(self):
407
    old_pool = [(1, 9)]
408
    self.cluster.uid_pool = list(old_pool)
409
    op = opcodes.OpClusterSetParams(add_uids=self.UID_POOL)
410
    self.ExecOpCode(op)
411
    self.assertEqual(set(self.UID_POOL + old_pool),
412
                     set(self.cluster.uid_pool))
413

    
414
  def testRemoveUids(self):
415
    additional_pool = [(1, 9)]
416
    self.cluster.uid_pool = self.UID_POOL + additional_pool
417
    op = opcodes.OpClusterSetParams(remove_uids=self.UID_POOL)
418
    self.ExecOpCode(op)
419
    self.assertEqual(additional_pool, self.cluster.uid_pool)
420

    
421
  def testMasterNetmask(self):
422
    op = opcodes.OpClusterSetParams(master_netmask=26)
423
    self.ExecOpCode(op)
424
    self.assertEqual(26, self.cluster.master_netmask)
425

    
426
  def testInvalidDiskparams(self):
427
    for diskparams in [{constants.DT_DISKLESS: {constants.LV_STRIPES: 0}},
428
                       {constants.DT_DRBD8: {constants.RBD_POOL: "pool"}},
429
                       {constants.DT_DRBD8: {constants.RBD_ACCESS: "bunny"}}]:
430
      self.ResetMocks()
431
      op = opcodes.OpClusterSetParams(diskparams=diskparams)
432
      self.ExecOpCodeExpectOpPrereqError(op, "verify diskparams")
433

    
434
  def testValidDiskparams(self):
435
    diskparams = {constants.DT_RBD: {constants.RBD_POOL: "mock_pool",
436
                                     constants.RBD_ACCESS: "kernelspace"}}
437
    op = opcodes.OpClusterSetParams(diskparams=diskparams)
438
    self.ExecOpCode(op)
439
    self.assertEqual(diskparams[constants.DT_RBD],
440
                     self.cluster.diskparams[constants.DT_RBD])
441

    
442
  def testMinimalDiskparams(self):
443
    diskparams = {constants.DT_RBD: {constants.RBD_POOL: "mock_pool"}}
444
    self.cluster.diskparams = {}
445
    op = opcodes.OpClusterSetParams(diskparams=diskparams)
446
    self.ExecOpCode(op)
447
    self.assertEqual(diskparams, self.cluster.diskparams)
448

    
449
  def testValidDiskparamsAccess(self):
450
    for value in constants.DISK_VALID_ACCESS_MODES:
451
      self.ResetMocks()
452
      op = opcodes.OpClusterSetParams(diskparams={
453
        constants.DT_RBD: {constants.RBD_ACCESS: value}
454
      })
455
      self.ExecOpCode(op)
456
      got = self.cluster.diskparams[constants.DT_RBD][constants.RBD_ACCESS]
457
      self.assertEqual(value, got)
458

    
459
  def testInvalidDiskparamsAccess(self):
460
    for value in ["default", "pinky_bunny"]:
461
      self.ResetMocks()
462
      op = opcodes.OpClusterSetParams(diskparams={
463
        constants.DT_RBD: {constants.RBD_ACCESS: value}
464
      })
465
      self.ExecOpCodeExpectOpPrereqError(op, "Invalid value of 'rbd:access'")
466

    
467
  def testUnsetDrbdHelperWithDrbdDisks(self):
468
    self.cfg.AddNewInstance(disks=[
469
      self.cfg.CreateDisk(dev_type=constants.DT_DRBD8, create_nodes=True)])
470
    op = opcodes.OpClusterSetParams(drbd_helper="")
471
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot disable drbd helper")
472

    
473
  def testFileStorageDir(self):
474
    op = opcodes.OpClusterSetParams(file_storage_dir="/random/path")
475
    self.ExecOpCode(op)
476

    
477
  def testSetFileStorageDirToCurrentValue(self):
478
    op = opcodes.OpClusterSetParams(
479
           file_storage_dir=self.cluster.file_storage_dir)
480
    self.ExecOpCode(op)
481

    
482
    self.mcpu.assertLogContainsRegex("file storage dir already set to value")
483

    
484
  def testValidDrbdHelper(self):
485
    node1 = self.cfg.AddNewNode()
486
    node1.offline = True
487
    self.rpc.call_drbd_helper.return_value = \
488
      self.RpcResultsBuilder() \
489
        .AddSuccessfulNode(self.master, "/bin/true") \
490
        .AddOfflineNode(node1) \
491
        .Build()
492
    op = opcodes.OpClusterSetParams(drbd_helper="/bin/true")
493
    self.ExecOpCode(op)
494
    self.mcpu.assertLogContainsRegex("Not checking drbd helper on offline node")
495

    
496
  def testDrbdHelperFailingNode(self):
497
    self.rpc.call_drbd_helper.return_value = \
498
      self.RpcResultsBuilder() \
499
        .AddFailedNode(self.master) \
500
        .Build()
501
    op = opcodes.OpClusterSetParams(drbd_helper="/bin/true")
502
    self.ExecOpCodeExpectOpPrereqError(op, "Error checking drbd helper")
503

    
504
  def testInvalidDrbdHelper(self):
505
    self.rpc.call_drbd_helper.return_value = \
506
      self.RpcResultsBuilder() \
507
        .AddSuccessfulNode(self.master, "/bin/false") \
508
        .Build()
509
    op = opcodes.OpClusterSetParams(drbd_helper="/bin/true")
510
    self.ExecOpCodeExpectOpPrereqError(op, "drbd helper is /bin/false")
511

    
512
  def testDrbdHelperWithoutDrbdDiskTemplate(self):
513
    drbd_helper = "/bin/random_helper"
514
    self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
515
    self.rpc.call_drbd_helper.return_value = \
516
      self.RpcResultsBuilder() \
517
        .AddSuccessfulNode(self.master, drbd_helper) \
518
        .Build()
519
    op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
520
    self.ExecOpCode(op)
521

    
522
    self.mcpu.assertLogContainsRegex("but did not enable")
523

    
524
  def testResetDrbdHelperDrbdDisabled(self):
525
    drbd_helper = ""
526
    self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
527
    op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
528
    self.ExecOpCode(op)
529

    
530
    self.assertEqual(None, self.cluster.drbd_usermode_helper)
531

    
532
  def testResetDrbdHelperDrbdEnabled(self):
533
    drbd_helper = ""
534
    self.cluster.enabled_disk_templates = [constants.DT_DRBD8]
535
    op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
536
    self.ExecOpCodeExpectOpPrereqError(
537
        op, "Cannot disable drbd helper while DRBD is enabled.")
538

    
539
  def testEnableDrbdNoHelper(self):
540
    self.cluster.enabled_disk_templates = [constants.DT_DISKLESS]
541
    self.cluster.drbd_usermode_helper = None
542
    enabled_disk_templates = [constants.DT_DRBD8]
543
    op = opcodes.OpClusterSetParams(
544
        enabled_disk_templates=enabled_disk_templates)
545
    self.ExecOpCodeExpectOpPrereqError(
546
        op, "Cannot enable DRBD without a DRBD usermode helper set")
547

    
548
  def testEnableDrbdHelperSet(self):
549
    drbd_helper = "/bin/random_helper"
550
    self.rpc.call_drbd_helper.return_value = \
551
      self.RpcResultsBuilder() \
552
        .AddSuccessfulNode(self.master, drbd_helper) \
553
        .Build()
554
    self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
555
    self.cluster.drbd_usermode_helper = drbd_helper
556
    enabled_disk_templates = [constants.DT_DRBD8]
557
    op = opcodes.OpClusterSetParams(
558
        enabled_disk_templates=enabled_disk_templates,
559
        ipolicy={constants.IPOLICY_DTS: enabled_disk_templates})
560
    self.ExecOpCode(op)
561

    
562
    self.assertEqual(drbd_helper, self.cluster.drbd_usermode_helper)
563

    
564
  def testDrbdHelperAlreadySet(self):
565
    drbd_helper = "/bin/true"
566
    self.rpc.call_drbd_helper.return_value = \
567
      self.RpcResultsBuilder() \
568
        .AddSuccessfulNode(self.master, "/bin/true") \
569
        .Build()
570
    self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
571
    op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
572
    self.ExecOpCode(op)
573

    
574
    self.assertEqual(drbd_helper, self.cluster.drbd_usermode_helper)
575
    self.mcpu.assertLogContainsRegex("DRBD helper already in desired state")
576

    
577
  def testSetDrbdHelper(self):
578
    drbd_helper = "/bin/true"
579
    self.rpc.call_drbd_helper.return_value = \
580
      self.RpcResultsBuilder() \
581
        .AddSuccessfulNode(self.master, "/bin/true") \
582
        .Build()
583
    self.cluster.drbd_usermode_helper = "/bin/false"
584
    self.cfg.SetEnabledDiskTemplates([constants.DT_DRBD8])
585
    op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
586
    self.ExecOpCode(op)
587

    
588
    self.assertEqual(drbd_helper, self.cluster.drbd_usermode_helper)
589

    
590
  def testBeparams(self):
591
    beparams = {constants.BE_VCPUS: 32}
592
    op = opcodes.OpClusterSetParams(beparams=beparams)
593
    self.ExecOpCode(op)
594
    self.assertEqual(32, self.cluster
595
                           .beparams[constants.PP_DEFAULT][constants.BE_VCPUS])
596

    
597
  def testNdparams(self):
598
    ndparams = {constants.ND_EXCLUSIVE_STORAGE: True}
599
    op = opcodes.OpClusterSetParams(ndparams=ndparams)
600
    self.ExecOpCode(op)
601
    self.assertEqual(True, self.cluster
602
                             .ndparams[constants.ND_EXCLUSIVE_STORAGE])
603

    
604
  def testNdparamsResetOobProgram(self):
605
    ndparams = {constants.ND_OOB_PROGRAM: ""}
606
    op = opcodes.OpClusterSetParams(ndparams=ndparams)
607
    self.ExecOpCode(op)
608
    self.assertEqual(constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM],
609
                     self.cluster.ndparams[constants.ND_OOB_PROGRAM])
610

    
611
  def testHvState(self):
612
    hv_state = {constants.HT_FAKE: {constants.HVST_CPU_TOTAL: 8}}
613
    op = opcodes.OpClusterSetParams(hv_state=hv_state)
614
    self.ExecOpCode(op)
615
    self.assertEqual(8, self.cluster.hv_state_static
616
                          [constants.HT_FAKE][constants.HVST_CPU_TOTAL])
617

    
618
  def testDiskState(self):
619
    disk_state = {
620
      constants.DT_PLAIN: {
621
        "mock_vg": {constants.DS_DISK_TOTAL: 10}
622
      }
623
    }
624
    op = opcodes.OpClusterSetParams(disk_state=disk_state)
625
    self.ExecOpCode(op)
626
    self.assertEqual(10, self.cluster
627
                           .disk_state_static[constants.DT_PLAIN]["mock_vg"]
628
                             [constants.DS_DISK_TOTAL])
629

    
630
  def testDefaultIPolicy(self):
631
    ipolicy = constants.IPOLICY_DEFAULTS
632
    op = opcodes.OpClusterSetParams(ipolicy=ipolicy)
633
    self.ExecOpCode(op)
634

    
635
  def testIPolicyNewViolation(self):
636
    import ganeti.constants as C
637
    ipolicy = C.IPOLICY_DEFAULTS
638
    ipolicy[C.ISPECS_MINMAX][0][C.ISPECS_MIN][C.ISPEC_MEM_SIZE] = 128
639
    ipolicy[C.ISPECS_MINMAX][0][C.ISPECS_MAX][C.ISPEC_MEM_SIZE] = 128
640

    
641
    self.cfg.AddNewInstance(beparams={C.BE_MINMEM: 512, C.BE_MAXMEM: 512})
642
    op = opcodes.OpClusterSetParams(ipolicy=ipolicy)
643
    self.ExecOpCode(op)
644

    
645
    self.mcpu.assertLogContainsRegex("instances violate them")
646

    
647
  def testNicparamsNoInstance(self):
648
    nicparams = {
649
      constants.NIC_LINK: "mock_bridge"
650
    }
651
    op = opcodes.OpClusterSetParams(nicparams=nicparams)
652
    self.ExecOpCode(op)
653

    
654
    self.assertEqual("mock_bridge",
655
                     self.cluster.nicparams
656
                       [constants.PP_DEFAULT][constants.NIC_LINK])
657

    
658
  def testNicparamsInvalidConf(self):
659
    nicparams = {
660
      constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
661
      constants.NIC_LINK: ""
662
    }
663
    op = opcodes.OpClusterSetParams(nicparams=nicparams)
664
    self.ExecOpCodeExpectException(op, errors.ConfigurationError, "NIC link")
665

    
666
  def testNicparamsInvalidInstanceConf(self):
667
    nicparams = {
668
      constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
669
      constants.NIC_LINK: "mock_bridge"
670
    }
671
    self.cfg.AddNewInstance(nics=[
672
      self.cfg.CreateNic(nicparams={constants.NIC_LINK: None})])
673
    op = opcodes.OpClusterSetParams(nicparams=nicparams)
674
    self.ExecOpCodeExpectOpPrereqError(op, "Missing bridged NIC link")
675

    
676
  def testNicparamsMissingIp(self):
677
    nicparams = {
678
      constants.NIC_MODE: constants.NIC_MODE_ROUTED
679
    }
680
    self.cfg.AddNewInstance()
681
    op = opcodes.OpClusterSetParams(nicparams=nicparams)
682
    self.ExecOpCodeExpectOpPrereqError(op, "routed NIC with no ip address")
683

    
684
  def testNicparamsWithInstance(self):
685
    nicparams = {
686
      constants.NIC_LINK: "mock_bridge"
687
    }
688
    self.cfg.AddNewInstance()
689
    op = opcodes.OpClusterSetParams(nicparams=nicparams)
690
    self.ExecOpCode(op)
691

    
692
  def testDefaultHvparams(self):
693
    hvparams = constants.HVC_DEFAULTS
694
    op = opcodes.OpClusterSetParams(hvparams=hvparams)
695
    self.ExecOpCode(op)
696

    
697
    self.assertEqual(hvparams, self.cluster.hvparams)
698

    
699
  def testMinimalHvparams(self):
700
    hvparams = {
701
      constants.HT_FAKE: {
702
        constants.HV_MIGRATION_MODE: constants.HT_MIGRATION_NONLIVE
703
      }
704
    }
705
    self.cluster.hvparams = {}
706
    op = opcodes.OpClusterSetParams(hvparams=hvparams)
707
    self.ExecOpCode(op)
708

    
709
    self.assertEqual(hvparams, self.cluster.hvparams)
710

    
711
  def testOsHvp(self):
712
    os_hvp = {
713
      "mocked_os": {
714
        constants.HT_FAKE: {
715
          constants.HV_MIGRATION_MODE: constants.HT_MIGRATION_NONLIVE
716
        }
717
      },
718
      "other_os": constants.HVC_DEFAULTS
719
    }
720
    op = opcodes.OpClusterSetParams(os_hvp=os_hvp)
721
    self.ExecOpCode(op)
722

    
723
    self.assertEqual(constants.HT_MIGRATION_NONLIVE,
724
                     self.cluster.os_hvp["mocked_os"][constants.HT_FAKE]
725
                       [constants.HV_MIGRATION_MODE])
726
    self.assertEqual(constants.HVC_DEFAULTS, self.cluster.os_hvp["other_os"])
727

    
728
  def testRemoveOsHvp(self):
729
    os_hvp = {"mocked_os": {constants.HT_FAKE: None}}
730
    op = opcodes.OpClusterSetParams(os_hvp=os_hvp)
731
    self.ExecOpCode(op)
732

    
733
    assert constants.HT_FAKE not in self.cluster.os_hvp["mocked_os"]
734

    
735
  def testDefaultOsHvp(self):
736
    os_hvp = {"mocked_os": constants.HVC_DEFAULTS.copy()}
737
    self.cluster.os_hvp = {"mocked_os": {}}
738
    op = opcodes.OpClusterSetParams(os_hvp=os_hvp)
739
    self.ExecOpCode(op)
740

    
741
    self.assertEqual(os_hvp, self.cluster.os_hvp)
742

    
743
  def testOsparams(self):
744
    osparams = {
745
      "mocked_os": {
746
        "param1": "value1",
747
        "param2": None
748
      },
749
      "other_os": {
750
        "param1": None
751
      }
752
    }
753
    self.cluster.osparams = {"other_os": {"param1": "value1"}}
754
    op = opcodes.OpClusterSetParams(osparams=osparams)
755
    self.ExecOpCode(op)
756

    
757
    self.assertEqual({"mocked_os": {"param1": "value1"}}, self.cluster.osparams)
758

    
759
  def testEnabledHypervisors(self):
760
    enabled_hypervisors = [constants.HT_XEN_HVM, constants.HT_XEN_PVM]
761
    op = opcodes.OpClusterSetParams(enabled_hypervisors=enabled_hypervisors)
762
    self.ExecOpCode(op)
763

    
764
    self.assertEqual(enabled_hypervisors, self.cluster.enabled_hypervisors)
765

    
766
  def testEnabledHypervisorsWithoutHypervisorParams(self):
767
    enabled_hypervisors = [constants.HT_FAKE]
768
    self.cluster.hvparams = {}
769
    op = opcodes.OpClusterSetParams(enabled_hypervisors=enabled_hypervisors)
770
    self.ExecOpCode(op)
771

    
772
    self.assertEqual(enabled_hypervisors, self.cluster.enabled_hypervisors)
773
    self.assertEqual(constants.HVC_DEFAULTS[constants.HT_FAKE],
774
                     self.cluster.hvparams[constants.HT_FAKE])
775

    
776
  @testutils.patch_object(utils, "FindFile")
777
  def testValidDefaultIallocator(self, find_file_mock):
778
    find_file_mock.return_value = "/random/path"
779
    default_iallocator = "/random/path"
780
    op = opcodes.OpClusterSetParams(default_iallocator=default_iallocator)
781
    self.ExecOpCode(op)
782

    
783
    self.assertEqual(default_iallocator, self.cluster.default_iallocator)
784

    
785
  @testutils.patch_object(utils, "FindFile")
786
  def testInvalidDefaultIallocator(self, find_file_mock):
787
    find_file_mock.return_value = None
788
    default_iallocator = "/random/path"
789
    op = opcodes.OpClusterSetParams(default_iallocator=default_iallocator)
790
    self.ExecOpCodeExpectOpPrereqError(op, "Invalid default iallocator script")
791

    
792
  def testEnabledDiskTemplates(self):
793
    enabled_disk_templates = [constants.DT_DISKLESS, constants.DT_PLAIN]
794
    op = opcodes.OpClusterSetParams(
795
           enabled_disk_templates=enabled_disk_templates,
796
           ipolicy={constants.IPOLICY_DTS: enabled_disk_templates})
797
    self.ExecOpCode(op)
798

    
799
    self.assertEqual(enabled_disk_templates,
800
                     self.cluster.enabled_disk_templates)
801

    
802
  def testDisablingDiskTemplatesOfInstances(self):
803
    old_disk_templates = [constants.DT_DISKLESS, constants.DT_PLAIN]
804
    self.cfg.SetEnabledDiskTemplates(old_disk_templates)
805
    self.cfg.AddNewInstance(
806
      disks=[self.cfg.CreateDisk(dev_type=constants.DT_PLAIN)])
807
    new_disk_templates = [constants.DT_DISKLESS, constants.DT_DRBD8]
808
    op = opcodes.OpClusterSetParams(
809
           enabled_disk_templates=new_disk_templates,
810
           ipolicy={constants.IPOLICY_DTS: new_disk_templates})
811
    self.ExecOpCodeExpectOpPrereqError(op, "least one instance using it")
812

    
813
  def testEnabledDiskTemplatesWithoutVgName(self):
814
    enabled_disk_templates = [constants.DT_PLAIN]
815
    self.cluster.volume_group_name = None
816
    op = opcodes.OpClusterSetParams(
817
           enabled_disk_templates=enabled_disk_templates)
818
    self.ExecOpCodeExpectOpPrereqError(op, "specify a volume group")
819

    
820
  def testDisableDiskTemplateWithExistingInstance(self):
821
    enabled_disk_templates = [constants.DT_DISKLESS]
822
    self.cfg.AddNewInstance(
823
      disks=[self.cfg.CreateDisk(dev_type=constants.DT_PLAIN)])
824
    op = opcodes.OpClusterSetParams(
825
           enabled_disk_templates=enabled_disk_templates,
826
           ipolicy={constants.IPOLICY_DTS: enabled_disk_templates})
827
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot disable disk template")
828

    
829
  def testVgNameNoLvmDiskTemplateEnabled(self):
830
    vg_name = "test_vg"
831
    self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
832
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
833
    self.ExecOpCode(op)
834

    
835
    self.assertEqual(vg_name, self.cluster.volume_group_name)
836
    self.mcpu.assertLogIsEmpty()
837

    
838
  def testUnsetVgNameWithLvmDiskTemplateEnabled(self):
839
    vg_name = ""
840
    self.cluster.enabled_disk_templates = [constants.DT_PLAIN]
841
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
842
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot unset volume group")
843

    
844
  def testUnsetVgNameWithLvmInstance(self):
845
    vg_name = ""
846
    self.cfg.AddNewInstance(
847
      disks=[self.cfg.CreateDisk(dev_type=constants.DT_PLAIN)])
848
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
849
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot unset volume group")
850

    
851
  def testUnsetVgNameWithNoLvmDiskTemplateEnabled(self):
852
    vg_name = ""
853
    self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
854
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
855
    self.ExecOpCode(op)
856

    
857
    self.assertEqual(None, self.cluster.volume_group_name)
858

    
859
  def testVgNameToOldName(self):
860
    vg_name = self.cluster.volume_group_name
861
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
862
    self.ExecOpCode(op)
863

    
864
    self.mcpu.assertLogContainsRegex("already in desired state")
865

    
866
  def testVgNameWithFailingNode(self):
867
    vg_name = "test_vg"
868
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
869
    self.rpc.call_vg_list.return_value = \
870
      self.RpcResultsBuilder() \
871
        .AddFailedNode(self.master) \
872
        .Build()
873
    self.ExecOpCode(op)
874

    
875
    self.mcpu.assertLogContainsRegex("Error while gathering data on node")
876

    
877
  def testVgNameWithValidNode(self):
878
    vg_name = "test_vg"
879
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
880
    self.rpc.call_vg_list.return_value = \
881
      self.RpcResultsBuilder() \
882
        .AddSuccessfulNode(self.master, {vg_name: 1024 * 1024}) \
883
        .Build()
884
    self.ExecOpCode(op)
885

    
886
  def testVgNameWithTooSmallNode(self):
887
    vg_name = "test_vg"
888
    op = opcodes.OpClusterSetParams(vg_name=vg_name)
889
    self.rpc.call_vg_list.return_value = \
890
      self.RpcResultsBuilder() \
891
        .AddSuccessfulNode(self.master, {vg_name: 1}) \
892
        .Build()
893
    self.ExecOpCodeExpectOpPrereqError(op, "too small")
894

    
895
  def testMiscParameters(self):
896
    op = opcodes.OpClusterSetParams(candidate_pool_size=123,
897
                                    maintain_node_health=True,
898
                                    modify_etc_hosts=True,
899
                                    prealloc_wipe_disks=True,
900
                                    reserved_lvs=["/dev/mock_lv"],
901
                                    use_external_mip_script=True)
902
    self.ExecOpCode(op)
903

    
904
    self.mcpu.assertLogIsEmpty()
905
    self.assertEqual(123, self.cluster.candidate_pool_size)
906
    self.assertEqual(True, self.cluster.maintain_node_health)
907
    self.assertEqual(True, self.cluster.modify_etc_hosts)
908
    self.assertEqual(True, self.cluster.prealloc_wipe_disks)
909
    self.assertEqual(["/dev/mock_lv"], self.cluster.reserved_lvs)
910
    self.assertEqual(True, self.cluster.use_external_mip_script)
911

    
912
  def testAddHiddenOs(self):
913
    self.cluster.hidden_os = ["hidden1", "hidden2"]
914
    op = opcodes.OpClusterSetParams(hidden_os=[(constants.DDM_ADD, "hidden2"),
915
                                               (constants.DDM_ADD, "hidden3")])
916
    self.ExecOpCode(op)
917

    
918
    self.assertEqual(["hidden1", "hidden2", "hidden3"], self.cluster.hidden_os)
919
    self.mcpu.assertLogContainsRegex("OS hidden2 already")
920

    
921
  def testRemoveBlacklistedOs(self):
922
    self.cluster.blacklisted_os = ["blisted1", "blisted2"]
923
    op = opcodes.OpClusterSetParams(blacklisted_os=[
924
                                      (constants.DDM_REMOVE, "blisted2"),
925
                                      (constants.DDM_REMOVE, "blisted3")])
926
    self.ExecOpCode(op)
927

    
928
    self.assertEqual(["blisted1"], self.cluster.blacklisted_os)
929
    self.mcpu.assertLogContainsRegex("OS blisted3 not found")
930

    
931
  def testMasterNetdev(self):
932
    master_netdev = "test_dev"
933
    op = opcodes.OpClusterSetParams(master_netdev=master_netdev)
934
    self.ExecOpCode(op)
935

    
936
    self.assertEqual(master_netdev, self.cluster.master_netdev)
937

    
938
  def testMasterNetdevFailNoForce(self):
939
    master_netdev = "test_dev"
940
    op = opcodes.OpClusterSetParams(master_netdev=master_netdev)
941
    self.rpc.call_node_deactivate_master_ip.return_value = \
942
      self.RpcResultsBuilder() \
943
        .CreateFailedNodeResult(self.master)
944
    self.ExecOpCodeExpectOpExecError(op, "Could not disable the master ip")
945

    
946
  def testMasterNetdevFailForce(self):
947
    master_netdev = "test_dev"
948
    op = opcodes.OpClusterSetParams(master_netdev=master_netdev,
949
                                    force=True)
950
    self.rpc.call_node_deactivate_master_ip.return_value = \
951
      self.RpcResultsBuilder() \
952
        .CreateFailedNodeResult(self.master)
953
    self.ExecOpCode(op)
954

    
955
    self.mcpu.assertLogContainsRegex("Could not disable the master ip")
956

    
957

    
958
class TestLUClusterVerify(CmdlibTestCase):
959
  def testVerifyAllGroups(self):
960
    op = opcodes.OpClusterVerify()
961
    result = self.ExecOpCode(op)
962

    
963
    self.assertEqual(2, len(result["jobs"]))
964

    
965
  def testVerifyDefaultGroups(self):
966
    op = opcodes.OpClusterVerify(group_name="default")
967
    result = self.ExecOpCode(op)
968

    
969
    self.assertEqual(1, len(result["jobs"]))
970

    
971

    
972
class TestLUClusterVerifyConfig(CmdlibTestCase):
973

    
974
  def setUp(self):
975
    super(TestLUClusterVerifyConfig, self).setUp()
976

    
977
    self._load_cert_patcher = testutils \
978
      .patch_object(OpenSSL.crypto, "load_certificate")
979
    self._load_cert_mock = self._load_cert_patcher.start()
980
    self._verify_cert_patcher = testutils \
981
      .patch_object(utils, "VerifyX509Certificate")
982
    self._verify_cert_mock = self._verify_cert_patcher.start()
983
    self._read_file_patcher = testutils.patch_object(utils, "ReadFile")
984
    self._read_file_mock = self._read_file_patcher.start()
985
    self._can_read_patcher = testutils.patch_object(utils, "CanRead")
986
    self._can_read_mock = self._can_read_patcher.start()
987

    
988
    self._can_read_mock.return_value = True
989
    self._read_file_mock.return_value = True
990
    self._verify_cert_mock.return_value = (None, "")
991
    self._load_cert_mock.return_value = True
992

    
993
  def tearDown(self):
994
    super(TestLUClusterVerifyConfig, self).tearDown()
995

    
996
    self._can_read_patcher.stop()
997
    self._read_file_patcher.stop()
998
    self._verify_cert_patcher.stop()
999
    self._load_cert_patcher.stop()
1000

    
1001
  def testSuccessfulRun(self):
1002
    self.cfg.AddNewInstance()
1003
    op = opcodes.OpClusterVerifyConfig()
1004
    result = self.ExecOpCode(op)
1005

    
1006
    self.assertTrue(result)
1007

    
1008
  def testDanglingNode(self):
1009
    node = self.cfg.AddNewNode()
1010
    self.cfg.AddNewInstance(primary_node=node)
1011
    node.group = "invalid"
1012
    op = opcodes.OpClusterVerifyConfig()
1013
    result = self.ExecOpCode(op)
1014

    
1015
    self.mcpu.assertLogContainsRegex(
1016
      "following nodes \(and their instances\) belong to a non existing group")
1017
    self.assertFalse(result)
1018

    
1019
  def testDanglingInstance(self):
1020
    inst = self.cfg.AddNewInstance()
1021
    inst.primary_node = "invalid"
1022
    op = opcodes.OpClusterVerifyConfig()
1023
    result = self.ExecOpCode(op)
1024

    
1025
    self.mcpu.assertLogContainsRegex(
1026
      "following instances have a non-existing primary-node")
1027
    self.assertFalse(result)
1028

    
1029

    
1030
class TestLUClusterVerifyGroup(CmdlibTestCase):
1031
  def testEmptyNodeGroup(self):
1032
    group = self.cfg.AddNewNodeGroup()
1033
    op = opcodes.OpClusterVerifyGroup(group_name=group.name, verbose=True)
1034

    
1035
    result = self.ExecOpCode(op)
1036

    
1037
    self.assertTrue(result)
1038
    self.mcpu.assertLogContainsRegex("Empty node group, skipping verification")
1039

    
1040
  def testSimpleInvocation(self):
1041
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1042

    
1043
    self.ExecOpCode(op)
1044

    
1045
  def testSimpleInvocationWithInstance(self):
1046
    self.cfg.AddNewInstance(disks=[])
1047
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1048

    
1049
    self.ExecOpCode(op)
1050

    
1051
  def testGhostNode(self):
1052
    group = self.cfg.AddNewNodeGroup()
1053
    node = self.cfg.AddNewNode(group=group.uuid, offline=True)
1054
    self.master.offline = True
1055
    self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
1056
                            primary_node=self.master,
1057
                            secondary_node=node)
1058

    
1059
    self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
1060
      RpcResultsBuilder() \
1061
        .AddOfflineNode(self.master) \
1062
        .Build()
1063

    
1064
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1065

    
1066
    self.ExecOpCode(op)
1067

    
1068
  def testValidRpcResult(self):
1069
    self.cfg.AddNewInstance(disks=[])
1070

    
1071
    self.rpc.call_node_verify.return_value = \
1072
      RpcResultsBuilder() \
1073
        .AddSuccessfulNode(self.master, {}) \
1074
        .Build()
1075

    
1076
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1077

    
1078
    self.ExecOpCode(op)
1079

    
1080

    
1081
class TestLUClusterVerifyGroupMethods(CmdlibTestCase):
1082
  """Base class for testing individual methods in LUClusterVerifyGroup.
1083

1084
  """
1085
  def setUp(self):
1086
    super(TestLUClusterVerifyGroupMethods, self).setUp()
1087
    self.op = opcodes.OpClusterVerifyGroup(group_name="default")
1088

    
1089
  def PrepareLU(self, lu):
1090
    lu._exclusive_storage = False
1091
    lu.master_node = self.master_uuid
1092
    lu.group_info = self.group
1093
    cluster.LUClusterVerifyGroup.all_node_info = \
1094
      property(fget=lambda _: self.cfg.GetAllNodesInfo())
1095

    
1096

    
1097
class TestLUClusterVerifyGroupVerifyNode(TestLUClusterVerifyGroupMethods):
1098
  @withLockedLU
1099
  def testInvalidNodeResult(self, lu):
1100
    self.assertFalse(lu._VerifyNode(self.master, None))
1101
    self.assertFalse(lu._VerifyNode(self.master, ""))
1102

    
1103
  @withLockedLU
1104
  def testInvalidVersion(self, lu):
1105
    self.assertFalse(lu._VerifyNode(self.master, {"version": None}))
1106
    self.assertFalse(lu._VerifyNode(self.master, {"version": ""}))
1107
    self.assertFalse(lu._VerifyNode(self.master, {
1108
      "version": (constants.PROTOCOL_VERSION - 1, constants.RELEASE_VERSION)
1109
    }))
1110

    
1111
    self.mcpu.ClearLogMessages()
1112
    self.assertTrue(lu._VerifyNode(self.master, {
1113
      "version": (constants.PROTOCOL_VERSION, constants.RELEASE_VERSION + "x")
1114
    }))
1115
    self.mcpu.assertLogContainsRegex("software version mismatch")
1116

    
1117
  def _GetValidNodeResult(self, additional_fields):
1118
    ret = {
1119
      "version": (constants.PROTOCOL_VERSION, constants.RELEASE_VERSION),
1120
      constants.NV_NODESETUP: []
1121
    }
1122
    ret.update(additional_fields)
1123
    return ret
1124

    
1125
  @withLockedLU
1126
  def testHypervisor(self, lu):
1127
    lu._VerifyNode(self.master, self._GetValidNodeResult({
1128
      constants.NV_HYPERVISOR: {
1129
        constants.HT_XEN_PVM: None,
1130
        constants.HT_XEN_HVM: "mock error"
1131
      }
1132
    }))
1133
    self.mcpu.assertLogContainsRegex(constants.HT_XEN_HVM)
1134
    self.mcpu.assertLogContainsRegex("mock error")
1135

    
1136
  @withLockedLU
1137
  def testHvParams(self, lu):
1138
    lu._VerifyNode(self.master, self._GetValidNodeResult({
1139
      constants.NV_HVPARAMS: [("mock item", constants.HT_XEN_HVM, "mock error")]
1140
    }))
1141
    self.mcpu.assertLogContainsRegex(constants.HT_XEN_HVM)
1142
    self.mcpu.assertLogContainsRegex("mock item")
1143
    self.mcpu.assertLogContainsRegex("mock error")
1144

    
1145
  @withLockedLU
1146
  def testSuccessfulResult(self, lu):
1147
    self.assertTrue(lu._VerifyNode(self.master, self._GetValidNodeResult({})))
1148
    self.mcpu.assertLogIsEmpty()
1149

    
1150

    
1151
class TestLUClusterVerifyGroupVerifyNodeTime(TestLUClusterVerifyGroupMethods):
1152
  @withLockedLU
1153
  def testInvalidNodeResult(self, lu):
1154
    for ndata in [{}, {constants.NV_TIME: "invalid"}]:
1155
      self.mcpu.ClearLogMessages()
1156
      lu._VerifyNodeTime(self.master, ndata, None, None)
1157

    
1158
      self.mcpu.assertLogContainsRegex("Node returned invalid time")
1159

    
1160
  @withLockedLU
1161
  def testNodeDiverges(self, lu):
1162
    for ntime in [(0, 0), (2000, 0)]:
1163
      self.mcpu.ClearLogMessages()
1164
      lu._VerifyNodeTime(self.master, {constants.NV_TIME: ntime}, 1000, 1005)
1165

    
1166
      self.mcpu.assertLogContainsRegex("Node time diverges")
1167

    
1168
  @withLockedLU
1169
  def testSuccessfulResult(self, lu):
1170
    lu._VerifyNodeTime(self.master, {constants.NV_TIME: (0, 0)}, 0, 5)
1171
    self.mcpu.assertLogIsEmpty()
1172

    
1173

    
1174
class TestLUClusterVerifyGroupUpdateVerifyNodeLVM(
1175
        TestLUClusterVerifyGroupMethods):
1176
  def setUp(self):
1177
    super(TestLUClusterVerifyGroupUpdateVerifyNodeLVM, self).setUp()
1178
    self.VALID_NRESULT = {
1179
      constants.NV_VGLIST: {"mock_vg": 30000},
1180
      constants.NV_PVLIST: [
1181
        {
1182
          "name": "mock_pv",
1183
          "vg_name": "mock_vg",
1184
          "size": 5000,
1185
          "free": 2500,
1186
          "attributes": [],
1187
          "lv_list": []
1188
        }
1189
      ]
1190
    }
1191

    
1192
  @withLockedLU
1193
  def testNoVgName(self, lu):
1194
    lu._UpdateVerifyNodeLVM(self.master, {}, None, None)
1195
    self.mcpu.assertLogIsEmpty()
1196

    
1197
  @withLockedLU
1198
  def testEmptyNodeResult(self, lu):
1199
    lu._UpdateVerifyNodeLVM(self.master, {}, "mock_vg", None)
1200
    self.mcpu.assertLogContainsRegex("unable to check volume groups")
1201
    self.mcpu.assertLogContainsRegex("Can't get PV list from node")
1202

    
1203
  @withLockedLU
1204
  def testValidNodeResult(self, lu):
1205
    lu._UpdateVerifyNodeLVM(self.master, self.VALID_NRESULT, "mock_vg", None)
1206
    self.mcpu.assertLogIsEmpty()
1207

    
1208
  @withLockedLU
1209
  def testValidNodeResultExclusiveStorage(self, lu):
1210
    lu._exclusive_storage = True
1211
    lu._UpdateVerifyNodeLVM(self.master, self.VALID_NRESULT, "mock_vg",
1212
                            cluster.LUClusterVerifyGroup.NodeImage())
1213
    self.mcpu.assertLogIsEmpty()
1214

    
1215

    
1216
class TestLUClusterVerifyGroupVerifyGroupDRBDVersion(
1217
        TestLUClusterVerifyGroupMethods):
1218
  @withLockedLU
1219
  def testEmptyNodeResult(self, lu):
1220
    lu._VerifyGroupDRBDVersion({})
1221
    self.mcpu.assertLogIsEmpty()
1222

    
1223
  @withLockedLU
1224
  def testValidNodeResult(self, lu):
1225
    lu._VerifyGroupDRBDVersion(
1226
      RpcResultsBuilder()
1227
        .AddSuccessfulNode(self.master, {
1228
          constants.NV_DRBDVERSION: "8.3.0"
1229
        })
1230
        .Build())
1231
    self.mcpu.assertLogIsEmpty()
1232

    
1233
  @withLockedLU
1234
  def testDifferentVersions(self, lu):
1235
    node1 = self.cfg.AddNewNode()
1236
    lu._VerifyGroupDRBDVersion(
1237
      RpcResultsBuilder()
1238
        .AddSuccessfulNode(self.master, {
1239
          constants.NV_DRBDVERSION: "8.3.0"
1240
        })
1241
        .AddSuccessfulNode(node1, {
1242
          constants.NV_DRBDVERSION: "8.4.0"
1243
        })
1244
        .Build())
1245
    self.mcpu.assertLogContainsRegex("DRBD version mismatch: 8.3.0")
1246
    self.mcpu.assertLogContainsRegex("DRBD version mismatch: 8.4.0")
1247

    
1248

    
1249
class TestLUClusterVerifyGroupVerifyGroupLVM(TestLUClusterVerifyGroupMethods):
1250
  @withLockedLU
1251
  def testNoVgName(self, lu):
1252
    lu._VerifyGroupLVM(None, None)
1253
    self.mcpu.assertLogIsEmpty()
1254

    
1255
  @withLockedLU
1256
  def testNoExclusiveStorage(self, lu):
1257
    lu._VerifyGroupLVM(None, "mock_vg")
1258
    self.mcpu.assertLogIsEmpty()
1259

    
1260
  @withLockedLU
1261
  def testNoPvInfo(self, lu):
1262
    lu._exclusive_storage = True
1263
    nimg = cluster.LUClusterVerifyGroup.NodeImage()
1264
    lu._VerifyGroupLVM({self.master.uuid: nimg}, "mock_vg")
1265
    self.mcpu.assertLogIsEmpty()
1266

    
1267
  @withLockedLU
1268
  def testValidPvInfos(self, lu):
1269
    lu._exclusive_storage = True
1270
    node2 = self.cfg.AddNewNode()
1271
    nimg1 = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master.uuid)
1272
    nimg1.pv_min = 10000
1273
    nimg1.pv_max = 10010
1274
    nimg2 = cluster.LUClusterVerifyGroup.NodeImage(uuid=node2.uuid)
1275
    nimg2.pv_min = 9998
1276
    nimg2.pv_max = 10005
1277
    lu._VerifyGroupLVM({self.master.uuid: nimg1, node2.uuid: nimg2}, "mock_vg")
1278
    self.mcpu.assertLogIsEmpty()
1279

    
1280

    
1281
class TestLUClusterVerifyGroupVerifyNodeBridges(
1282
        TestLUClusterVerifyGroupMethods):
1283
  @withLockedLU
1284
  def testNoBridges(self, lu):
1285
    lu._VerifyNodeBridges(None, None, None)
1286
    self.mcpu.assertLogIsEmpty()
1287

    
1288
  @withLockedLU
1289
  def testInvalidBridges(self, lu):
1290
    for ndata in [{}, {constants.NV_BRIDGES: ""}]:
1291
      self.mcpu.ClearLogMessages()
1292
      lu._VerifyNodeBridges(self.master, ndata, ["mock_bridge"])
1293
      self.mcpu.assertLogContainsRegex("not return valid bridge information")
1294

    
1295
    self.mcpu.ClearLogMessages()
1296
    lu._VerifyNodeBridges(self.master, {constants.NV_BRIDGES: ["mock_bridge"]},
1297
                          ["mock_bridge"])
1298
    self.mcpu.assertLogContainsRegex("missing bridge")
1299

    
1300

    
1301
class TestLUClusterVerifyGroupVerifyNodeUserScripts(
1302
        TestLUClusterVerifyGroupMethods):
1303
  @withLockedLU
1304
  def testNoUserScripts(self, lu):
1305
    lu._VerifyNodeUserScripts(self.master, {})
1306
    self.mcpu.assertLogContainsRegex("did not return user scripts information")
1307

    
1308
  @withLockedLU
1309
  def testBrokenUserScripts(self, lu):
1310
    lu._VerifyNodeUserScripts(self.master,
1311
                              {constants.NV_USERSCRIPTS: ["script"]})
1312
    self.mcpu.assertLogContainsRegex("scripts not present or not executable")
1313

    
1314

    
1315
class TestLUClusterVerifyGroupVerifyNodeNetwork(
1316
        TestLUClusterVerifyGroupMethods):
1317

    
1318
  def setUp(self):
1319
    super(TestLUClusterVerifyGroupVerifyNodeNetwork, self).setUp()
1320
    self.VALID_NRESULT = {
1321
      constants.NV_NODELIST: {},
1322
      constants.NV_NODENETTEST: {},
1323
      constants.NV_MASTERIP: True
1324
    }
1325

    
1326
  @withLockedLU
1327
  def testEmptyNodeResult(self, lu):
1328
    lu._VerifyNodeNetwork(self.master, {})
1329
    self.mcpu.assertLogContainsRegex(
1330
      "node hasn't returned node ssh connectivity data")
1331
    self.mcpu.assertLogContainsRegex(
1332
      "node hasn't returned node tcp connectivity data")
1333
    self.mcpu.assertLogContainsRegex(
1334
      "node hasn't returned node master IP reachability data")
1335

    
1336
  @withLockedLU
1337
  def testValidResult(self, lu):
1338
    lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1339
    self.mcpu.assertLogIsEmpty()
1340

    
1341
  @withLockedLU
1342
  def testSshProblem(self, lu):
1343
    self.VALID_NRESULT.update({
1344
      constants.NV_NODELIST: {
1345
        "mock_node": "mock_error"
1346
      }
1347
    })
1348
    lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1349
    self.mcpu.assertLogContainsRegex("ssh communication with node 'mock_node'")
1350

    
1351
  @withLockedLU
1352
  def testTcpProblem(self, lu):
1353
    self.VALID_NRESULT.update({
1354
      constants.NV_NODENETTEST: {
1355
        "mock_node": "mock_error"
1356
      }
1357
    })
1358
    lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1359
    self.mcpu.assertLogContainsRegex("tcp communication with node 'mock_node'")
1360

    
1361
  @withLockedLU
1362
  def testMasterIpNotReachable(self, lu):
1363
    self.VALID_NRESULT.update({
1364
      constants.NV_MASTERIP: False
1365
    })
1366
    node1 = self.cfg.AddNewNode()
1367
    lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1368
    self.mcpu.assertLogContainsRegex(
1369
      "the master node cannot reach the master IP")
1370

    
1371
    self.mcpu.ClearLogMessages()
1372
    lu._VerifyNodeNetwork(node1, self.VALID_NRESULT)
1373
    self.mcpu.assertLogContainsRegex("cannot reach the master IP")
1374

    
1375

    
1376
class TestLUClusterVerifyGroupVerifyInstance(TestLUClusterVerifyGroupMethods):
1377
  def setUp(self):
1378
    super(TestLUClusterVerifyGroupVerifyInstance, self).setUp()
1379

    
1380
    self.node1 = self.cfg.AddNewNode()
1381
    self.drbd_inst = self.cfg.AddNewInstance(
1382
      disks=[self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
1383
                                 primary_node=self.master,
1384
                                 secondary_node=self.node1)])
1385
    self.running_inst = self.cfg.AddNewInstance(
1386
      admin_state=constants.ADMINST_UP, disks_active=True)
1387
    self.diskless_inst = self.cfg.AddNewInstance(disks=[])
1388

    
1389
    self.master_img = \
1390
      cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1391
    self.master_img.volumes = ["/".join(disk.logical_id)
1392
                               for inst in [self.running_inst,
1393
                                            self.diskless_inst]
1394
                               for disk in inst.disks]
1395
    self.master_img.volumes.extend(
1396
      ["/".join(disk.logical_id) for disk in self.drbd_inst.disks[0].children])
1397
    self.master_img.instances = [self.running_inst.uuid]
1398
    self.node1_img = \
1399
      cluster.LUClusterVerifyGroup.NodeImage(uuid=self.node1.uuid)
1400
    self.node1_img.volumes = \
1401
      ["/".join(disk.logical_id) for disk in self.drbd_inst.disks[0].children]
1402
    self.node_imgs = {
1403
      self.master_uuid: self.master_img,
1404
      self.node1.uuid: self.node1_img
1405
    }
1406
    self.diskstatus = {
1407
      self.master_uuid: [
1408
        (True, objects.BlockDevStatus(ldisk_status=constants.LDS_OKAY))
1409
        for _ in self.running_inst.disks
1410
      ]
1411
    }
1412

    
1413
  @withLockedLU
1414
  def testDisklessInst(self, lu):
1415
    lu._VerifyInstance(self.diskless_inst, self.node_imgs, {})
1416
    self.mcpu.assertLogIsEmpty()
1417

    
1418
  @withLockedLU
1419
  def testOfflineNode(self, lu):
1420
    self.master_img.offline = True
1421
    lu._VerifyInstance(self.drbd_inst, self.node_imgs, {})
1422
    self.mcpu.assertLogIsEmpty()
1423

    
1424
  @withLockedLU
1425
  def testRunningOnOfflineNode(self, lu):
1426
    self.master_img.offline = True
1427
    lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1428
    self.mcpu.assertLogContainsRegex(
1429
      "instance is marked as running and lives on offline node")
1430

    
1431
  @withLockedLU
1432
  def testMissingVolume(self, lu):
1433
    self.master_img.volumes = []
1434
    lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1435
    self.mcpu.assertLogContainsRegex("volume .* missing")
1436

    
1437
  @withLockedLU
1438
  def testRunningInstanceOnWrongNode(self, lu):
1439
    self.master_img.instances = []
1440
    self.diskless_inst.admin_state = constants.ADMINST_UP
1441
    lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1442
    self.mcpu.assertLogContainsRegex("instance not running on its primary node")
1443

    
1444
  @withLockedLU
1445
  def testRunningInstanceOnRightNode(self, lu):
1446
    self.master_img.instances = [self.running_inst.uuid]
1447
    lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1448
    self.mcpu.assertLogIsEmpty()
1449

    
1450
  @withLockedLU
1451
  def testValidDiskStatus(self, lu):
1452
    lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1453
    self.mcpu.assertLogIsEmpty()
1454

    
1455
  @withLockedLU
1456
  def testDegradedDiskStatus(self, lu):
1457
    self.diskstatus[self.master_uuid][0][1].is_degraded = True
1458
    lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1459
    self.mcpu.assertLogContainsRegex("instance .* is degraded")
1460

    
1461
  @withLockedLU
1462
  def testNotOkayDiskStatus(self, lu):
1463
    self.diskstatus[self.master_uuid][0][1].ldisk_status = constants.LDS_FAULTY
1464
    lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1465
    self.mcpu.assertLogContainsRegex("instance .* state is 'faulty'")
1466

    
1467
  @withLockedLU
1468
  def testExclusiveStorageWithInvalidInstance(self, lu):
1469
    self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
1470
    lu._VerifyInstance(self.drbd_inst, self.node_imgs, self.diskstatus)
1471
    self.mcpu.assertLogContainsRegex(
1472
      "instance has template drbd, which is not supported")
1473

    
1474
  @withLockedLU
1475
  def testExclusiveStorageWithValidInstance(self, lu):
1476
    self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
1477
    self.running_inst.disks[0].spindles = 1
1478
    lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1479
    self.mcpu.assertLogIsEmpty()
1480

    
1481
  @withLockedLU
1482
  def testDrbdInTwoGroups(self, lu):
1483
    group = self.cfg.AddNewNodeGroup()
1484
    self.node1.group = group.uuid
1485
    lu._VerifyInstance(self.drbd_inst, self.node_imgs, self.diskstatus)
1486
    self.mcpu.assertLogContainsRegex(
1487
      "instance has primary and secondary nodes in different groups")
1488

    
1489
  @withLockedLU
1490
  def testOfflineSecondary(self, lu):
1491
    self.node1_img.offline = True
1492
    lu._VerifyInstance(self.drbd_inst, self.node_imgs, self.diskstatus)
1493
    self.mcpu.assertLogContainsRegex("instance has offline secondary node\(s\)")
1494

    
1495

    
1496
class TestLUClusterVerifyGroupVerifyOrphanVolumes(
1497
        TestLUClusterVerifyGroupMethods):
1498
  @withLockedLU
1499
  def testOrphanedVolume(self, lu):
1500
    master_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1501
    master_img.volumes = ["mock_vg/disk_0", "mock_vg/disk_1", "mock_vg/disk_2"]
1502
    node_imgs = {
1503
      self.master_uuid: master_img
1504
    }
1505
    node_vol_should = {
1506
      self.master_uuid: ["mock_vg/disk_0"]
1507
    }
1508

    
1509
    lu._VerifyOrphanVolumes(node_vol_should, node_imgs,
1510
                            utils.FieldSet("mock_vg/disk_2"))
1511
    self.mcpu.assertLogContainsRegex("volume mock_vg/disk_1 is unknown")
1512
    self.mcpu.assertLogDoesNotContainRegex("volume mock_vg/disk_0 is unknown")
1513
    self.mcpu.assertLogDoesNotContainRegex("volume mock_vg/disk_2 is unknown")
1514

    
1515

    
1516
class TestLUClusterVerifyGroupVerifyNPlusOneMemory(
1517
        TestLUClusterVerifyGroupMethods):
1518
  @withLockedLU
1519
  def testN1Failure(self, lu):
1520
    group1 = self.cfg.AddNewNodeGroup()
1521

    
1522
    node1 = self.cfg.AddNewNode()
1523
    node2 = self.cfg.AddNewNode(group=group1)
1524
    node3 = self.cfg.AddNewNode()
1525

    
1526
    inst1 = self.cfg.AddNewInstance()
1527
    inst2 = self.cfg.AddNewInstance()
1528
    inst3 = self.cfg.AddNewInstance()
1529

    
1530
    node1_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=node1.uuid)
1531
    node1_img.sbp = {
1532
      self.master_uuid: [inst1.uuid, inst2.uuid, inst3.uuid]
1533
    }
1534

    
1535
    node2_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=node2.uuid)
1536

    
1537
    node3_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=node3.uuid)
1538
    node3_img.offline = True
1539

    
1540
    node_imgs = {
1541
      node1.uuid: node1_img,
1542
      node2.uuid: node2_img,
1543
      node3.uuid: node3_img
1544
    }
1545

    
1546
    lu._VerifyNPlusOneMemory(node_imgs, self.cfg.GetAllInstancesInfo())
1547
    self.mcpu.assertLogContainsRegex(
1548
      "not enough memory to accomodate instance failovers")
1549

    
1550
    self.mcpu.ClearLogMessages()
1551
    node1_img.mfree = 1000
1552
    lu._VerifyNPlusOneMemory(node_imgs, self.cfg.GetAllInstancesInfo())
1553
    self.mcpu.assertLogIsEmpty()
1554

    
1555

    
1556
class TestLUClusterVerifyGroupVerifyFiles(TestLUClusterVerifyGroupMethods):
1557
  @withLockedLU
1558
  def test(self, lu):
1559
    node1 = self.cfg.AddNewNode(master_candidate=False, offline=False,
1560
                                vm_capable=True)
1561
    node2 = self.cfg.AddNewNode(master_candidate=True, vm_capable=False)
1562
    node3 = self.cfg.AddNewNode(master_candidate=False, offline=False,
1563
                                vm_capable=True)
1564
    node4 = self.cfg.AddNewNode(master_candidate=False, offline=False,
1565
                                vm_capable=True)
1566
    node5 = self.cfg.AddNewNode(master_candidate=False, offline=True)
1567

    
1568
    nodeinfo = [self.master, node1, node2, node3, node4, node5]
1569
    files_all = set([
1570
      pathutils.CLUSTER_DOMAIN_SECRET_FILE,
1571
      pathutils.RAPI_CERT_FILE,
1572
      pathutils.RAPI_USERS_FILE,
1573
      ])
1574
    files_opt = set([
1575
      pathutils.RAPI_USERS_FILE,
1576
      hv_xen.XL_CONFIG_FILE,
1577
      pathutils.VNC_PASSWORD_FILE,
1578
      ])
1579
    files_mc = set([
1580
      pathutils.CLUSTER_CONF_FILE,
1581
      ])
1582
    files_vm = set([
1583
      hv_xen.XEND_CONFIG_FILE,
1584
      hv_xen.XL_CONFIG_FILE,
1585
      pathutils.VNC_PASSWORD_FILE,
1586
      ])
1587
    nvinfo = RpcResultsBuilder() \
1588
      .AddSuccessfulNode(self.master, {
1589
        constants.NV_FILELIST: {
1590
          pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
1591
          pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
1592
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
1593
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
1594
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
1595
        }}) \
1596
      .AddSuccessfulNode(node1, {
1597
        constants.NV_FILELIST: {
1598
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
1599
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
1600
          }
1601
        }) \
1602
      .AddSuccessfulNode(node2, {
1603
        constants.NV_FILELIST: {
1604
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
1605
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
1606
          }
1607
        }) \
1608
      .AddSuccessfulNode(node3, {
1609
        constants.NV_FILELIST: {
1610
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
1611
          pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
1612
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
1613
          pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
1614
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
1615
          }
1616
        }) \
1617
      .AddSuccessfulNode(node4, {}) \
1618
      .AddOfflineNode(node5) \
1619
      .Build()
1620
    assert set(nvinfo.keys()) == set(map(operator.attrgetter("uuid"), nodeinfo))
1621

    
1622
    lu._VerifyFiles(nodeinfo, self.master_uuid, nvinfo,
1623
                    (files_all, files_opt, files_mc, files_vm))
1624

    
1625
    expected_msgs = [
1626
      "File %s found with 2 different checksums (variant 1 on"
1627
        " %s, %s, %s; variant 2 on %s)" %
1628
        (pathutils.RAPI_CERT_FILE, node1.name, node2.name, node3.name,
1629
         self.master.name),
1630
      "File %s is missing from node(s) %s" %
1631
        (pathutils.CLUSTER_DOMAIN_SECRET_FILE, node1.name),
1632
      "File %s should not exist on node(s) %s" %
1633
        (pathutils.CLUSTER_CONF_FILE, node3.name),
1634
      "File %s is missing from node(s) %s" %
1635
        (hv_xen.XEND_CONFIG_FILE, node3.name),
1636
      "File %s is missing from node(s) %s" %
1637
        (pathutils.CLUSTER_CONF_FILE, node2.name),
1638
      "File %s found with 2 different checksums (variant 1 on"
1639
        " %s; variant 2 on %s)" %
1640
        (pathutils.CLUSTER_CONF_FILE, self.master.name, node3.name),
1641
      "File %s is optional, but it must exist on all or no nodes (not"
1642
        " found on %s, %s, %s)" %
1643
        (pathutils.RAPI_USERS_FILE, self.master.name, node1.name, node2.name),
1644
      "File %s is optional, but it must exist on all or no nodes (not"
1645
        " found on %s)" % (hv_xen.XL_CONFIG_FILE, node1.name),
1646
      "Node did not return file checksum data",
1647
      ]
1648

    
1649
    self.assertEqual(len(self.mcpu.GetLogMessages()), len(expected_msgs))
1650
    for expected_msg in expected_msgs:
1651
      self.mcpu.assertLogContainsInLine(expected_msg)
1652

    
1653

    
1654
class TestLUClusterVerifyGroupVerifyNodeDrbd(TestLUClusterVerifyGroupMethods):
1655
  def setUp(self):
1656
    super(TestLUClusterVerifyGroupVerifyNodeDrbd, self).setUp()
1657

    
1658
    self.node1 = self.cfg.AddNewNode()
1659
    self.node2 = self.cfg.AddNewNode()
1660
    self.inst = self.cfg.AddNewInstance(
1661
      disks=[self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
1662
                                 primary_node=self.node1,
1663
                                 secondary_node=self.node2)],
1664
      admin_state=constants.ADMINST_UP)
1665

    
1666
  @withLockedLU
1667
  def testNoDrbdHelper(self, lu):
1668
    lu._VerifyNodeDrbd(self.master, {}, self.cfg.GetAllInstancesInfo(), None,
1669
                       self.cfg.ComputeDRBDMap())
1670
    self.mcpu.assertLogIsEmpty()
1671

    
1672
  @withLockedLU
1673
  def testDrbdHelperInvalidNodeResult(self, lu):
1674
    for ndata, expected in [({}, "no drbd usermode helper returned"),
1675
                            ({constants.NV_DRBDHELPER: (False, "")},
1676
                             "drbd usermode helper check unsuccessful"),
1677
                            ({constants.NV_DRBDHELPER: (True, "/bin/false")},
1678
                             "wrong drbd usermode helper")]:
1679
      self.mcpu.ClearLogMessages()
1680
      lu._VerifyNodeDrbd(self.master, ndata, self.cfg.GetAllInstancesInfo(),
1681
                         "/bin/true", self.cfg.ComputeDRBDMap())
1682
      self.mcpu.assertLogContainsRegex(expected)
1683

    
1684
  @withLockedLU
1685
  def testNoNodeResult(self, lu):
1686
    lu._VerifyNodeDrbd(self.node1, {}, self.cfg.GetAllInstancesInfo(),
1687
                         None, self.cfg.ComputeDRBDMap())
1688
    self.mcpu.assertLogContainsRegex("drbd minor 1 of .* is not active")
1689

    
1690
  @withLockedLU
1691
  def testInvalidNodeResult(self, lu):
1692
    lu._VerifyNodeDrbd(self.node1, {constants.NV_DRBDLIST: ""},
1693
                       self.cfg.GetAllInstancesInfo(), None,
1694
                       self.cfg.ComputeDRBDMap())
1695
    self.mcpu.assertLogContainsRegex("cannot parse drbd status file")
1696

    
1697
  @withLockedLU
1698
  def testWrongMinorInUse(self, lu):
1699
    lu._VerifyNodeDrbd(self.node1, {constants.NV_DRBDLIST: [2]},
1700
                       self.cfg.GetAllInstancesInfo(), None,
1701
                       self.cfg.ComputeDRBDMap())
1702
    self.mcpu.assertLogContainsRegex("drbd minor 1 of .* is not active")
1703
    self.mcpu.assertLogContainsRegex("unallocated drbd minor 2 is in use")
1704

    
1705
  @withLockedLU
1706
  def testValidResult(self, lu):
1707
    lu._VerifyNodeDrbd(self.node1, {constants.NV_DRBDLIST: [1]},
1708
                       self.cfg.GetAllInstancesInfo(), None,
1709
                       self.cfg.ComputeDRBDMap())
1710
    self.mcpu.assertLogIsEmpty()
1711

    
1712

    
1713
class TestLUClusterVerifyGroupVerifyNodeOs(TestLUClusterVerifyGroupMethods):
1714
  @withLockedLU
1715
  def testUpdateNodeOsInvalidNodeResult(self, lu):
1716
    for ndata in [{}, {constants.NV_OSLIST: ""}, {constants.NV_OSLIST: [""]},
1717
                  {constants.NV_OSLIST: [["1", "2"]]}]:
1718
      self.mcpu.ClearLogMessages()
1719
      nimage = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1720
      lu._UpdateNodeOS(self.master, ndata, nimage)
1721
      self.mcpu.assertLogContainsRegex("node hasn't returned valid OS data")
1722

    
1723
  @withLockedLU
1724
  def testUpdateNodeOsValidNodeResult(self, lu):
1725
    ndata = {
1726
      constants.NV_OSLIST: [
1727
        ["mock_OS", "/mocked/path", True, "", ["default"], [],
1728
         [constants.OS_API_V20]],
1729
        ["Another_Mock", "/random", True, "", ["var1", "var2"],
1730
         [{"param1": "val1"}, {"param2": "val2"}], constants.OS_API_VERSIONS]
1731
      ]
1732
    }
1733
    nimage = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1734
    lu._UpdateNodeOS(self.master, ndata, nimage)
1735
    self.mcpu.assertLogIsEmpty()
1736

    
1737
  @withLockedLU
1738
  def testVerifyNodeOs(self, lu):
1739
    node = self.cfg.AddNewNode()
1740
    nimg_root = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1741
    nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=node.uuid)
1742

    
1743
    nimg_root.os_fail = False
1744
    nimg_root.oslist = {
1745
      "mock_os": [("/mocked/path", True, "", set(["default"]), set(),
1746
                   set([constants.OS_API_V20]))],
1747
      "broken_base_os": [("/broken", False, "", set(), set(),
1748
                         set([constants.OS_API_V20]))],
1749
      "only_on_root": [("/random", True, "", set(), set(), set())],
1750
      "diffing_os": [("/pinky", True, "", set(["var1", "var2"]),
1751
                      set([("param1", "val1"), ("param2", "val2")]),
1752
                      set([constants.OS_API_V20]))]
1753
    }
1754
    nimg.os_fail = False
1755
    nimg.oslist = {
1756
      "mock_os": [("/mocked/path", True, "", set(["default"]), set(),
1757
                   set([constants.OS_API_V20]))],
1758
      "only_on_test": [("/random", True, "", set(), set(), set())],
1759
      "diffing_os": [("/bunny", True, "", set(["var1", "var3"]),
1760
                      set([("param1", "val1"), ("param3", "val3")]),
1761
                      set([constants.OS_API_V15]))],
1762
      "broken_os": [("/broken", False, "", set(), set(),
1763
                     set([constants.OS_API_V20]))],
1764
      "multi_entries": [
1765
        ("/multi1", True, "", set(), set(), set([constants.OS_API_V20])),
1766
        ("/multi2", True, "", set(), set(), set([constants.OS_API_V20]))]
1767
    }
1768

    
1769
    lu._VerifyNodeOS(node, nimg, nimg_root)
1770

    
1771
    expected_msgs = [
1772
      "Extra OS only_on_test not present on reference node",
1773
      "OSes present on reference node .* but missing on this node:" +
1774
        " only_on_root",
1775
      "OS API version for diffing_os differs",
1776
      "OS variants list for diffing_os differs",
1777
      "OS parameters for diffing_os differs",
1778
      "Invalid OS broken_os",
1779
      "Extra OS broken_os not present on reference node",
1780
      "OS 'multi_entries' has multiple entries",
1781
      "Extra OS multi_entries not present on reference node"
1782
    ]
1783

    
1784
    self.assertEqual(len(expected_msgs), len(self.mcpu.GetLogMessages()))
1785
    for expected_msg in expected_msgs:
1786
      self.mcpu.assertLogContainsRegex(expected_msg)
1787

    
1788

    
1789
class TestLUClusterVerifyGroupVerifyAcceptedFileStoragePaths(
1790
  TestLUClusterVerifyGroupMethods):
1791
  @withLockedLU
1792
  def testNotMaster(self, lu):
1793
    lu._VerifyAcceptedFileStoragePaths(self.master, {}, False)
1794
    self.mcpu.assertLogIsEmpty()
1795

    
1796
  @withLockedLU
1797
  def testNotMasterButRetunedValue(self, lu):
1798
    lu._VerifyAcceptedFileStoragePaths(
1799
      self.master, {constants.NV_ACCEPTED_STORAGE_PATHS: []}, False)
1800
    self.mcpu.assertLogContainsRegex(
1801
      "Node should not have returned forbidden file storage paths")
1802

    
1803
  @withLockedLU
1804
  def testMasterInvalidNodeResult(self, lu):
1805
    lu._VerifyAcceptedFileStoragePaths(self.master, {}, True)
1806
    self.mcpu.assertLogContainsRegex(
1807
      "Node did not return forbidden file storage paths")
1808

    
1809
  @withLockedLU
1810
  def testMasterForbiddenPaths(self, lu):
1811
    lu._VerifyAcceptedFileStoragePaths(
1812
      self.master, {constants.NV_ACCEPTED_STORAGE_PATHS: ["/forbidden"]}, True)
1813
    self.mcpu.assertLogContainsRegex("Found forbidden file storage paths")
1814

    
1815
  @withLockedLU
1816
  def testMasterSuccess(self, lu):
1817
    lu._VerifyAcceptedFileStoragePaths(
1818
      self.master, {constants.NV_ACCEPTED_STORAGE_PATHS: []}, True)
1819
    self.mcpu.assertLogIsEmpty()
1820

    
1821

    
1822
class TestLUClusterVerifyGroupVerifyStoragePaths(
1823
  TestLUClusterVerifyGroupMethods):
1824
  @withLockedLU
1825
  def testVerifyFileStoragePathsSuccess(self, lu):
1826
    lu._VerifyFileStoragePaths(self.master, {})
1827
    self.mcpu.assertLogIsEmpty()
1828

    
1829
  @withLockedLU
1830
  def testVerifyFileStoragePathsFailure(self, lu):
1831
    lu._VerifyFileStoragePaths(self.master,
1832
                               {constants.NV_FILE_STORAGE_PATH: "/fail/path"})
1833
    self.mcpu.assertLogContainsRegex(
1834
      "The configured file storage path is unusable")
1835

    
1836
  @withLockedLU
1837
  def testVerifySharedFileStoragePathsSuccess(self, lu):
1838
    lu._VerifySharedFileStoragePaths(self.master, {})
1839
    self.mcpu.assertLogIsEmpty()
1840

    
1841
  @withLockedLU
1842
  def testVerifySharedFileStoragePathsFailure(self, lu):
1843
    lu._VerifySharedFileStoragePaths(
1844
      self.master, {constants.NV_SHARED_FILE_STORAGE_PATH: "/fail/path"})
1845
    self.mcpu.assertLogContainsRegex(
1846
      "The configured sharedfile storage path is unusable")
1847

    
1848

    
1849
class TestLUClusterVerifyGroupVerifyOob(TestLUClusterVerifyGroupMethods):
1850
  @withLockedLU
1851
  def testEmptyResult(self, lu):
1852
    lu._VerifyOob(self.master, {})
1853
    self.mcpu.assertLogIsEmpty()
1854

    
1855
  @withLockedLU
1856
  def testErrorResults(self, lu):
1857
    lu._VerifyOob(self.master, {constants.NV_OOB_PATHS: ["path1", "path2"]})
1858
    self.mcpu.assertLogContainsRegex("path1")
1859
    self.mcpu.assertLogContainsRegex("path2")
1860

    
1861

    
1862
class TestLUClusterVerifyGroupUpdateNodeVolumes(
1863
  TestLUClusterVerifyGroupMethods):
1864
  def setUp(self):
1865
    super(TestLUClusterVerifyGroupUpdateNodeVolumes, self).setUp()
1866
    self.nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1867

    
1868
  @withLockedLU
1869
  def testNoVgName(self, lu):
1870
    lu._UpdateNodeVolumes(self.master, {}, self.nimg, None)
1871
    self.mcpu.assertLogIsEmpty()
1872
    self.assertTrue(self.nimg.lvm_fail)
1873

    
1874
  @withLockedLU
1875
  def testErrorMessage(self, lu):
1876
    lu._UpdateNodeVolumes(self.master, {constants.NV_LVLIST: "mock error"},
1877
                          self.nimg, "mock_vg")
1878
    self.mcpu.assertLogContainsRegex("LVM problem on node: mock error")
1879
    self.assertTrue(self.nimg.lvm_fail)
1880

    
1881
  @withLockedLU
1882
  def testInvalidNodeResult(self, lu):
1883
    lu._UpdateNodeVolumes(self.master, {constants.NV_LVLIST: [1, 2, 3]},
1884
                          self.nimg, "mock_vg")
1885
    self.mcpu.assertLogContainsRegex("rpc call to node failed")
1886
    self.assertTrue(self.nimg.lvm_fail)
1887

    
1888
  @withLockedLU
1889
  def testValidNodeResult(self, lu):
1890
    lu._UpdateNodeVolumes(self.master, {constants.NV_LVLIST: {}},
1891
                          self.nimg, "mock_vg")
1892
    self.mcpu.assertLogIsEmpty()
1893
    self.assertFalse(self.nimg.lvm_fail)
1894

    
1895

    
1896
class TestLUClusterVerifyGroupUpdateNodeInstances(
1897
  TestLUClusterVerifyGroupMethods):
1898
  def setUp(self):
1899
    super(TestLUClusterVerifyGroupUpdateNodeInstances, self).setUp()
1900
    self.nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1901

    
1902
  @withLockedLU
1903
  def testInvalidNodeResult(self, lu):
1904
    lu._UpdateNodeInstances(self.master, {}, self.nimg)
1905
    self.mcpu.assertLogContainsRegex("rpc call to node failed")
1906

    
1907
  @withLockedLU
1908
  def testValidNodeResult(self, lu):
1909
    inst = self.cfg.AddNewInstance()
1910
    lu._UpdateNodeInstances(self.master,
1911
                            {constants.NV_INSTANCELIST: [inst.name]},
1912
                            self.nimg)
1913
    self.mcpu.assertLogIsEmpty()
1914

    
1915

    
1916
class TestLUClusterVerifyGroupUpdateNodeInfo(TestLUClusterVerifyGroupMethods):
1917
  def setUp(self):
1918
    super(TestLUClusterVerifyGroupUpdateNodeInfo, self).setUp()
1919
    self.nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1920
    self.valid_hvresult = {constants.NV_HVINFO: {"memory_free": 1024}}
1921

    
1922
  @withLockedLU
1923
  def testInvalidHvNodeResult(self, lu):
1924
    for ndata in [{}, {constants.NV_HVINFO: ""}]:
1925
      self.mcpu.ClearLogMessages()
1926
      lu._UpdateNodeInfo(self.master, ndata, self.nimg, None)
1927
      self.mcpu.assertLogContainsRegex("rpc call to node failed")
1928

    
1929
  @withLockedLU
1930
  def testInvalidMemoryFreeHvNodeResult(self, lu):
1931
    lu._UpdateNodeInfo(self.master,
1932
                       {constants.NV_HVINFO: {"memory_free": "abc"}},
1933
                       self.nimg, None)
1934
    self.mcpu.assertLogContainsRegex(
1935
      "node returned invalid nodeinfo, check hypervisor")
1936

    
1937
  @withLockedLU
1938
  def testValidHvNodeResult(self, lu):
1939
    lu._UpdateNodeInfo(self.master, self.valid_hvresult, self.nimg, None)
1940
    self.mcpu.assertLogIsEmpty()
1941

    
1942
  @withLockedLU
1943
  def testInvalidVgNodeResult(self, lu):
1944
    for vgdata in [[], ""]:
1945
      self.mcpu.ClearLogMessages()
1946
      ndata = {constants.NV_VGLIST: vgdata}
1947
      ndata.update(self.valid_hvresult)
1948
      lu._UpdateNodeInfo(self.master, ndata, self.nimg, "mock_vg")
1949
      self.mcpu.assertLogContainsRegex(
1950
        "node didn't return data for the volume group 'mock_vg'")
1951

    
1952
  @withLockedLU
1953
  def testInvalidDiskFreeVgNodeResult(self, lu):
1954
    self.valid_hvresult.update({
1955
      constants.NV_VGLIST: {"mock_vg": "abc"}
1956
    })
1957
    lu._UpdateNodeInfo(self.master, self.valid_hvresult, self.nimg, "mock_vg")
1958
    self.mcpu.assertLogContainsRegex(
1959
      "node returned invalid LVM info, check LVM status")
1960

    
1961
  @withLockedLU
1962
  def testValidVgNodeResult(self, lu):
1963
    self.valid_hvresult.update({
1964
      constants.NV_VGLIST: {"mock_vg": 10000}
1965
    })
1966
    lu._UpdateNodeInfo(self.master, self.valid_hvresult, self.nimg, "mock_vg")
1967
    self.mcpu.assertLogIsEmpty()
1968

    
1969

    
1970
class TestLUClusterVerifyGroupCollectDiskInfo(TestLUClusterVerifyGroupMethods):
1971
  def setUp(self):
1972
    super(TestLUClusterVerifyGroupCollectDiskInfo, self).setUp()
1973

    
1974
    self.node1 = self.cfg.AddNewNode()
1975
    self.node2 = self.cfg.AddNewNode()
1976
    self.node3 = self.cfg.AddNewNode()
1977

    
1978
    self.diskless_inst = \
1979
      self.cfg.AddNewInstance(primary_node=self.node1,
1980
                              disk_template=constants.DT_DISKLESS)
1981
    self.plain_inst = \
1982
      self.cfg.AddNewInstance(primary_node=self.node2,
1983
                              disk_template=constants.DT_PLAIN)
1984
    self.drbd_inst = \
1985
      self.cfg.AddNewInstance(primary_node=self.node3,
1986
                              secondary_node=self.node2,
1987
                              disk_template=constants.DT_DRBD8)
1988

    
1989
    self.node1_img = cluster.LUClusterVerifyGroup.NodeImage(
1990
                       uuid=self.node1.uuid)
1991
    self.node1_img.pinst = [self.diskless_inst.uuid]
1992
    self.node1_img.sinst = []
1993
    self.node2_img = cluster.LUClusterVerifyGroup.NodeImage(
1994
                       uuid=self.node2.uuid)
1995
    self.node2_img.pinst = [self.plain_inst.uuid]
1996
    self.node2_img.sinst = [self.drbd_inst.uuid]
1997
    self.node3_img = cluster.LUClusterVerifyGroup.NodeImage(
1998
                       uuid=self.node3.uuid)
1999
    self.node3_img.pinst = [self.drbd_inst.uuid]
2000
    self.node3_img.sinst = []
2001

    
2002
    self.node_images = {
2003
      self.node1.uuid: self.node1_img,
2004
      self.node2.uuid: self.node2_img,
2005
      self.node3.uuid: self.node3_img
2006
    }
2007

    
2008
    self.node_uuids = [self.node1.uuid, self.node2.uuid, self.node3.uuid]
2009

    
2010
  @withLockedLU
2011
  def testSuccessfulRun(self, lu):
2012
    self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
2013
      RpcResultsBuilder() \
2014
        .AddSuccessfulNode(self.node2, [(True, ""), (True, "")]) \
2015
        .AddSuccessfulNode(self.node3, [(True, "")]) \
2016
        .Build()
2017

    
2018
    lu._CollectDiskInfo(self.node_uuids, self.node_images,
2019
                        self.cfg.GetAllInstancesInfo())
2020

    
2021
    self.mcpu.assertLogIsEmpty()
2022

    
2023
  @withLockedLU
2024
  def testOfflineAndFailingNodes(self, lu):
2025
    self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
2026
      RpcResultsBuilder() \
2027
        .AddOfflineNode(self.node2) \
2028
        .AddFailedNode(self.node3) \
2029
        .Build()
2030

    
2031
    lu._CollectDiskInfo(self.node_uuids, self.node_images,
2032
                        self.cfg.GetAllInstancesInfo())
2033

    
2034
    self.mcpu.assertLogContainsRegex("while getting disk information")
2035

    
2036
  @withLockedLU
2037
  def testInvalidNodeResult(self, lu):
2038
    self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
2039
      RpcResultsBuilder() \
2040
        .AddSuccessfulNode(self.node2, [(True,), (False,)]) \
2041
        .AddSuccessfulNode(self.node3, [""]) \
2042
        .Build()
2043

    
2044
    lu._CollectDiskInfo(self.node_uuids, self.node_images,
2045
                        self.cfg.GetAllInstancesInfo())
2046
    # logging is not performed through mcpu
2047
    self.mcpu.assertLogIsEmpty()
2048

    
2049

    
2050
class TestLUClusterVerifyGroupHooksCallBack(TestLUClusterVerifyGroupMethods):
2051
  def setUp(self):
2052
    super(TestLUClusterVerifyGroupHooksCallBack, self).setUp()
2053

    
2054
    self.feedback_fn = lambda _: None
2055

    
2056
  def PrepareLU(self, lu):
2057
    super(TestLUClusterVerifyGroupHooksCallBack, self).PrepareLU(lu)
2058

    
2059
    lu.my_node_uuids = list(self.cfg.GetAllNodesInfo().keys())
2060

    
2061
  @withLockedLU
2062
  def testEmptyGroup(self, lu):
2063
    lu.my_node_uuids = []
2064
    lu.HooksCallBack(constants.HOOKS_PHASE_POST, None, self.feedback_fn, None)
2065

    
2066
  @withLockedLU
2067
  def testFailedResult(self, lu):
2068
    lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2069
                     RpcResultsBuilder(use_node_names=True)
2070
                       .AddFailedNode(self.master).Build(),
2071
                     self.feedback_fn,
2072
                     None)
2073
    self.mcpu.assertLogContainsRegex("Communication failure in hooks execution")
2074

    
2075
  @withLockedLU
2076
  def testOfflineNode(self, lu):
2077
    lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2078
                     RpcResultsBuilder(use_node_names=True)
2079
                       .AddOfflineNode(self.master).Build(),
2080
                     self.feedback_fn,
2081
                     None)
2082

    
2083
  @withLockedLU
2084
  def testValidResult(self, lu):
2085
    lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2086
                     RpcResultsBuilder(use_node_names=True)
2087
                       .AddSuccessfulNode(self.master,
2088
                                          [("mock_script",
2089
                                            constants.HKR_SUCCESS,
2090
                                            "mock output")])
2091
                       .Build(),
2092
                     self.feedback_fn,
2093
                     None)
2094

    
2095
  @withLockedLU
2096
  def testFailedScriptResult(self, lu):
2097
    lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2098
                     RpcResultsBuilder(use_node_names=True)
2099
                       .AddSuccessfulNode(self.master,
2100
                                          [("mock_script",
2101
                                            constants.HKR_FAIL,
2102
                                            "mock output")])
2103
                       .Build(),
2104
                     self.feedback_fn,
2105
                     None)
2106
    self.mcpu.assertLogContainsRegex("Script mock_script failed")
2107

    
2108

    
2109
class TestLUClusterVerifyDisks(CmdlibTestCase):
2110
  def testVerifyDisks(self):
2111
    op = opcodes.OpClusterVerifyDisks()
2112
    result = self.ExecOpCode(op)
2113

    
2114
    self.assertEqual(1, len(result["jobs"]))
2115

    
2116

    
2117
if __name__ == "__main__":
2118
  testutils.GanetiTestProgram()