Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / instance_unittest.py @ 4869595d

History | View | Annotate | Download (83.9 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 LUInstance*
23

24
"""
25

    
26
import copy
27
import itertools
28
import unittest
29
import mock
30
import operator
31

    
32
from ganeti import compat
33
from ganeti import constants
34
from ganeti import errors
35
from ganeti import ht
36
from ganeti import opcodes
37
from ganeti import objects
38
from ganeti.rpc import node as rpc
39
from ganeti import utils
40
from ganeti.cmdlib import instance
41

    
42
from cmdlib.cmdlib_unittest import _StubComputeIPolicySpecViolation, _FakeLU
43

    
44
from testsupport import *
45

    
46
import testutils
47

    
48

    
49
class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
50
  def test(self):
51
    ispec = {
52
      constants.ISPEC_MEM_SIZE: 2048,
53
      constants.ISPEC_CPU_COUNT: 2,
54
      constants.ISPEC_DISK_COUNT: 1,
55
      constants.ISPEC_DISK_SIZE: [512],
56
      constants.ISPEC_NIC_COUNT: 0,
57
      constants.ISPEC_SPINDLE_USE: 1,
58
      }
59
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
60
                                            constants.DT_PLAIN)
61
    ret = instance._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
62
                                                        constants.DT_PLAIN,
63
                                                        _compute_fn=stub)
64
    self.assertEqual(ret, [])
65

    
66

    
67
class TestLUInstanceCreate(CmdlibTestCase):
68
  def setUp(self):
69
    super(TestLUInstanceCreate, self).setUp()
70

    
71
    self.net = self.cfg.AddNewNetwork()
72
    self.cfg.ConnectNetworkToGroup(self.net, self.group)
73

    
74
    self.node1 = self.cfg.AddNewNode()
75
    self.node2 = self.cfg.AddNewNode()
76

    
77
    self.rpc.call_os_get.side_effect = \
78
      lambda node, _: self.RpcResultsBuilder() \
79
                        .CreateSuccessfulNodeResult(node, self.os)
80

    
81
    hv_info = ("bootid",
82
               [{
83
                 "type": constants.ST_LVM_VG,
84
                 "storage_free": 10000
85
               }],
86
               ({"memory_free": 10000}, ))
87
    self.rpc.call_node_info.return_value = \
88
      self.RpcResultsBuilder() \
89
        .AddSuccessfulNode(self.master, hv_info) \
90
        .AddSuccessfulNode(self.node1, hv_info) \
91
        .AddSuccessfulNode(self.node2, hv_info) \
92
        .Build()
93

    
94
    self.rpc.call_blockdev_getmirrorstatus.side_effect = \
95
      lambda node, _: self.RpcResultsBuilder() \
96
                        .CreateSuccessfulNodeResult(node, [])
97

    
98
    self.iallocator_cls.return_value.result = [self.node1.name, self.node2.name]
99

    
100
    self.diskless_op = opcodes.OpInstanceCreate(
101
      instance_name="diskless.test.com",
102
      pnode=self.master.name,
103
      disk_template=constants.DT_DISKLESS,
104
      mode=constants.INSTANCE_CREATE,
105
      nics=[{}],
106
      disks=[],
107
      os_type=self.os_name_variant)
108

    
109
    self.plain_op = opcodes.OpInstanceCreate(
110
      instance_name="plain.test.com",
111
      pnode=self.master.name,
112
      disk_template=constants.DT_PLAIN,
113
      mode=constants.INSTANCE_CREATE,
114
      nics=[{}],
115
      disks=[{
116
        constants.IDISK_SIZE: 1024
117
      }],
118
      os_type=self.os_name_variant)
119

    
120
    self.block_op = opcodes.OpInstanceCreate(
121
      instance_name="block.test.com",
122
      pnode=self.master.name,
123
      disk_template=constants.DT_BLOCK,
124
      mode=constants.INSTANCE_CREATE,
125
      nics=[{}],
126
      disks=[{
127
        constants.IDISK_SIZE: 1024,
128
        constants.IDISK_ADOPT: "/dev/disk/block0"
129
      }],
130
      os_type=self.os_name_variant)
131

    
132
    self.drbd_op = opcodes.OpInstanceCreate(
133
      instance_name="drbd.test.com",
134
      pnode=self.node1.name,
135
      snode=self.node2.name,
136
      disk_template=constants.DT_DRBD8,
137
      mode=constants.INSTANCE_CREATE,
138
      nics=[{}],
139
      disks=[{
140
        constants.IDISK_SIZE: 1024
141
      }],
142
      os_type=self.os_name_variant)
143

    
144
    self.file_op = opcodes.OpInstanceCreate(
145
      instance_name="file.test.com",
146
      pnode=self.node1.name,
147
      disk_template=constants.DT_FILE,
148
      mode=constants.INSTANCE_CREATE,
149
      nics=[{}],
150
      disks=[{
151
        constants.IDISK_SIZE: 1024
152
      }],
153
      os_type=self.os_name_variant)
154

    
155
  def testSimpleCreate(self):
156
    op = self.CopyOpCode(self.diskless_op)
157
    self.ExecOpCode(op)
158

    
159
  def testStrangeHostnameResolve(self):
160
    op = self.CopyOpCode(self.diskless_op)
161
    self.netutils_mod.GetHostname.return_value = \
162
      HostnameMock("random.host.example.com", "203.0.113.1")
163
    self.ExecOpCodeExpectOpPrereqError(
164
      op, "Resolved hostname .* does not look the same as given hostname")
165

    
166
  def testOpportunisticLockingNoIAllocator(self):
167
    op = self.CopyOpCode(self.diskless_op,
168
                         opportunistic_locking=True,
169
                         iallocator=None)
170
    self.ExecOpCodeExpectOpPrereqError(
171
      op, "Opportunistic locking is only available in combination with an"
172
          " instance allocator")
173

    
174
  def testNicWithNetAndMode(self):
175
    op = self.CopyOpCode(self.diskless_op,
176
                         nics=[{
177
                           constants.INIC_NETWORK: self.net.name,
178
                           constants.INIC_MODE: constants.NIC_MODE_BRIDGED
179
                         }])
180
    self.ExecOpCodeExpectOpPrereqError(
181
      op, "If network is given, no mode or link is allowed to be passed")
182

    
183
  def testAutoIpNoNameCheck(self):
184
    op = self.CopyOpCode(self.diskless_op,
185
                         nics=[{
186
                           constants.INIC_IP: constants.VALUE_AUTO
187
                         }],
188
                         ip_check=False,
189
                         name_check=False)
190
    self.ExecOpCodeExpectOpPrereqError(
191
      op, "IP address set to auto but name checks have been skipped")
192

    
193
  def testAutoIp(self):
194
    op = self.CopyOpCode(self.diskless_op,
195
                         nics=[{
196
                           constants.INIC_IP: constants.VALUE_AUTO
197
                         }])
198
    self.ExecOpCode(op)
199

    
200
  def testPoolIpNoNetwork(self):
201
    op = self.CopyOpCode(self.diskless_op,
202
                         nics=[{
203
                           constants.INIC_IP: constants.NIC_IP_POOL
204
                         }])
205
    self.ExecOpCodeExpectOpPrereqError(
206
      op, "if ip=pool, parameter network must be passed too")
207

    
208
  def testValidIp(self):
209
    op = self.CopyOpCode(self.diskless_op,
210
                         nics=[{
211
                           constants.INIC_IP: "203.0.113.1"
212
                         }])
213
    self.ExecOpCode(op)
214

    
215
  def testRoutedNoIp(self):
216
    op = self.CopyOpCode(self.diskless_op,
217
                         nics=[{
218
                           constants.INIC_MODE: constants.NIC_MODE_ROUTED
219
                         }])
220
    self.ExecOpCodeExpectOpPrereqError(
221
      op, "Routed nic mode requires an ip address")
222

    
223
  def testValicMac(self):
224
    op = self.CopyOpCode(self.diskless_op,
225
                         nics=[{
226
                           constants.INIC_MAC: "f0:df:f4:a3:d1:cf"
227
                         }])
228
    self.ExecOpCode(op)
229

    
230
  def testValidNicParams(self):
231
    op = self.CopyOpCode(self.diskless_op,
232
                         nics=[{
233
                           constants.INIC_MODE: constants.NIC_MODE_BRIDGED,
234
                           constants.INIC_LINK: "br_mock"
235
                         }])
236
    self.ExecOpCode(op)
237

    
238
  def testValidNicParamsOpenVSwitch(self):
239
    op = self.CopyOpCode(self.diskless_op,
240
                         nics=[{
241
                           constants.INIC_MODE: constants.NIC_MODE_OVS,
242
                           constants.INIC_VLAN: "1"
243
                         }])
244
    self.ExecOpCode(op)
245

    
246
  def testNicNoneName(self):
247
    op = self.CopyOpCode(self.diskless_op,
248
                         nics=[{
249
                           constants.INIC_NAME: constants.VALUE_NONE
250
                         }])
251
    self.ExecOpCode(op)
252

    
253
  def testConflictingIP(self):
254
    op = self.CopyOpCode(self.diskless_op,
255
                         nics=[{
256
                           constants.INIC_IP: self.net.gateway[:-1] + "2"
257
                         }])
258
    self.ExecOpCodeExpectOpPrereqError(
259
      op, "The requested IP address .* belongs to network .*, but the target"
260
          " NIC does not.")
261

    
262
  def testVLanFormat(self):
263
    for vlan in [".pinky", ":bunny", ":1:pinky", "bunny"]:
264
      self.ResetMocks()
265
      op = self.CopyOpCode(self.diskless_op,
266
                           nics=[{
267
                             constants.INIC_VLAN: vlan
268
                           }])
269
      self.ExecOpCodeExpectOpPrereqError(
270
        op, "Specified VLAN parameter is invalid")
271

    
272
  def testPoolIp(self):
273
    op = self.CopyOpCode(self.diskless_op,
274
                         nics=[{
275
                           constants.INIC_IP: constants.NIC_IP_POOL,
276
                           constants.INIC_NETWORK: self.net.name
277
                         }])
278
    self.ExecOpCode(op)
279

    
280
  def testPoolIpUnconnectedNetwork(self):
281
    net = self.cfg.AddNewNetwork()
282
    op = self.CopyOpCode(self.diskless_op,
283
                         nics=[{
284
                           constants.INIC_IP: constants.NIC_IP_POOL,
285
                           constants.INIC_NETWORK: net.name
286
                         }])
287
    self.ExecOpCodeExpectOpPrereqError(
288
      op, "No netparams found for network .*.")
289

    
290
  def testIpNotInNetwork(self):
291
    op = self.CopyOpCode(self.diskless_op,
292
                         nics=[{
293
                           constants.INIC_IP: "203.0.113.1",
294
                           constants.INIC_NETWORK: self.net.name
295
                         }])
296
    self.ExecOpCodeExpectOpPrereqError(
297
      op, "IP address .* already in use or does not belong to network .*")
298

    
299
  def testMixAdoptAndNotAdopt(self):
300
    op = self.CopyOpCode(self.diskless_op,
301
                         disk_template=constants.DT_PLAIN,
302
                         disks=[{
303
                           constants.IDISK_ADOPT: "lv1"
304
                         }, {}])
305
    self.ExecOpCodeExpectOpPrereqError(
306
      op, "Either all disks are adopted or none is")
307

    
308
  def testMustAdoptWithoutAdopt(self):
309
    op = self.CopyOpCode(self.diskless_op,
310
                         disk_template=constants.DT_BLOCK,
311
                         disks=[{}])
312
    self.ExecOpCodeExpectOpPrereqError(
313
      op, "Disk template blockdev requires disk adoption, but no 'adopt'"
314
          " parameter given")
315

    
316
  def testDontAdoptWithAdopt(self):
317
    op = self.CopyOpCode(self.diskless_op,
318
                         disk_template=constants.DT_DRBD8,
319
                         disks=[{
320
                           constants.IDISK_ADOPT: "lv1"
321
                         }])
322
    self.ExecOpCodeExpectOpPrereqError(
323
      op, "Disk adoption is not supported for the 'drbd' disk template")
324

    
325
  def testAdoptWithIAllocator(self):
326
    op = self.CopyOpCode(self.diskless_op,
327
                         disk_template=constants.DT_PLAIN,
328
                         disks=[{
329
                           constants.IDISK_ADOPT: "lv1"
330
                         }],
331
                         iallocator="mock")
332
    self.ExecOpCodeExpectOpPrereqError(
333
      op, "Disk adoption not allowed with an iallocator script")
334

    
335
  def testAdoptWithImport(self):
336
    op = self.CopyOpCode(self.diskless_op,
337
                         disk_template=constants.DT_PLAIN,
338
                         disks=[{
339
                           constants.IDISK_ADOPT: "lv1"
340
                         }],
341
                         mode=constants.INSTANCE_IMPORT)
342
    self.ExecOpCodeExpectOpPrereqError(
343
      op, "Disk adoption not allowed for instance import")
344

    
345
  def testArgumentCombinations(self):
346
    op = self.CopyOpCode(self.diskless_op,
347
                         # start flag will be flipped
348
                         no_install=True,
349
                         start=True,
350
                         # no allowed combination
351
                         ip_check=True,
352
                         name_check=False)
353
    self.ExecOpCodeExpectOpPrereqError(
354
      op, "Cannot do IP address check without a name check")
355

    
356
  def testInvalidFileDriver(self):
357
    op = self.CopyOpCode(self.diskless_op,
358
                         file_driver="invalid_file_driver")
359
    self.ExecOpCodeExpectOpPrereqError(
360
      op, "Parameter 'OP_INSTANCE_CREATE.file_driver' fails validation")
361

    
362
  def testMissingSecondaryNode(self):
363
    op = self.CopyOpCode(self.diskless_op,
364
                         pnode=self.master.name,
365
                         disk_template=constants.DT_DRBD8)
366
    self.ExecOpCodeExpectOpPrereqError(
367
      op, "The networked disk templates need a mirror node")
368

    
369
  def testIgnoredSecondaryNode(self):
370
    op = self.CopyOpCode(self.diskless_op,
371
                         pnode=self.master.name,
372
                         snode=self.node1.name,
373
                         disk_template=constants.DT_PLAIN)
374
    try:
375
      self.ExecOpCode(op)
376
    except Exception:
377
      pass
378
    self.mcpu.assertLogContainsRegex(
379
      "Secondary node will be ignored on non-mirrored disk template")
380

    
381
  def testMissingOsType(self):
382
    op = self.CopyOpCode(self.diskless_op,
383
                         os_type=self.REMOVE)
384
    self.ExecOpCodeExpectOpPrereqError(op, "No guest OS specified")
385

    
386
  def testBlacklistedOs(self):
387
    self.cluster.blacklisted_os = [self.os_name_variant]
388
    op = self.CopyOpCode(self.diskless_op)
389
    self.ExecOpCodeExpectOpPrereqError(
390
      op, "Guest OS .* is not allowed for installation")
391

    
392
  def testMissingDiskTemplate(self):
393
    self.cluster.enabled_disk_templates = [constants.DT_DISKLESS]
394
    op = self.CopyOpCode(self.diskless_op,
395
                         disk_template=self.REMOVE)
396
    self.ExecOpCode(op)
397

    
398
  def testExistingInstance(self):
399
    inst = self.cfg.AddNewInstance()
400
    op = self.CopyOpCode(self.diskless_op,
401
                         instance_name=inst.name)
402
    self.ExecOpCodeExpectOpPrereqError(
403
      op, "Instance .* is already in the cluster")
404

    
405
  def testPlainInstance(self):
406
    op = self.CopyOpCode(self.plain_op)
407
    self.ExecOpCode(op)
408

    
409
  def testPlainIAllocator(self):
410
    op = self.CopyOpCode(self.plain_op,
411
                         pnode=self.REMOVE,
412
                         iallocator="mock")
413
    self.ExecOpCode(op)
414

    
415
  def testIAllocatorOpportunisticLocking(self):
416
    op = self.CopyOpCode(self.plain_op,
417
                         pnode=self.REMOVE,
418
                         iallocator="mock",
419
                         opportunistic_locking=True)
420
    self.ExecOpCode(op)
421

    
422
  def testFailingIAllocator(self):
423
    self.iallocator_cls.return_value.success = False
424
    op = self.CopyOpCode(self.plain_op,
425
                         pnode=self.REMOVE,
426
                         iallocator="mock")
427
    self.ExecOpCodeExpectOpPrereqError(
428
      op, "Can't compute nodes using iallocator")
429

    
430
  def testDrbdInstance(self):
431
    op = self.CopyOpCode(self.drbd_op)
432
    self.ExecOpCode(op)
433

    
434
  def testDrbdIAllocator(self):
435
    op = self.CopyOpCode(self.drbd_op,
436
                         pnode=self.REMOVE,
437
                         snode=self.REMOVE,
438
                         iallocator="mock")
439
    self.ExecOpCode(op)
440

    
441
  def testFileInstance(self):
442
    op = self.CopyOpCode(self.file_op)
443
    self.ExecOpCode(op)
444

    
445
  def testFileInstanceNoClusterStorage(self):
446
    self.cluster.file_storage_dir = None
447
    op = self.CopyOpCode(self.file_op)
448
    self.ExecOpCodeExpectOpPrereqError(
449
      op, "Cluster file storage dir for 'file' storage type not defined")
450

    
451
  def testFileInstanceAdditionalPath(self):
452
    op = self.CopyOpCode(self.file_op,
453
                         file_storage_dir="mock_dir")
454
    self.ExecOpCode(op)
455

    
456
  def testIdentifyDefaults(self):
457
    op = self.CopyOpCode(self.plain_op,
458
                         hvparams={
459
                           constants.HV_BOOT_ORDER: "cd"
460
                         },
461
                         beparams=constants.BEC_DEFAULTS.copy(),
462
                         nics=[{
463
                           constants.NIC_MODE: constants.NIC_MODE_BRIDGED
464
                         }],
465
                         osparams={
466
                           self.os_name_variant: {}
467
                         },
468
                         identify_defaults=True)
469
    self.ExecOpCode(op)
470

    
471
    inst = self.cfg.GetAllInstancesInfo().values()[0]
472
    self.assertEqual(0, len(inst.hvparams))
473
    self.assertEqual(0, len(inst.beparams))
474
    assert self.os_name_variant not in inst.osparams or \
475
            len(inst.osparams[self.os_name_variant]) == 0
476

    
477
  def testOfflineNode(self):
478
    self.node1.offline = True
479
    op = self.CopyOpCode(self.diskless_op,
480
                         pnode=self.node1.name)
481
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot use offline primary node")
482

    
483
  def testDrainedNode(self):
484
    self.node1.drained = True
485
    op = self.CopyOpCode(self.diskless_op,
486
                         pnode=self.node1.name)
487
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot use drained primary node")
488

    
489
  def testNonVmCapableNode(self):
490
    self.node1.vm_capable = False
491
    op = self.CopyOpCode(self.diskless_op,
492
                         pnode=self.node1.name)
493
    self.ExecOpCodeExpectOpPrereqError(
494
      op, "Cannot use non-vm_capable primary node")
495

    
496
  def testNonEnabledHypervisor(self):
497
    self.cluster.enabled_hypervisors = [constants.HT_XEN_HVM]
498
    op = self.CopyOpCode(self.diskless_op,
499
                         hypervisor=constants.HT_FAKE)
500
    self.ExecOpCodeExpectOpPrereqError(
501
      op, "Selected hypervisor .* not enabled in the cluster")
502

    
503
  def testAddTag(self):
504
    op = self.CopyOpCode(self.diskless_op,
505
                         tags=["tag"])
506
    self.ExecOpCode(op)
507

    
508
  def testInvalidTag(self):
509
    op = self.CopyOpCode(self.diskless_op,
510
                         tags=["too_long" * 20])
511
    self.ExecOpCodeExpectException(op, errors.TagError, "Tag too long")
512

    
513
  def testPingableInstanceName(self):
514
    self.netutils_mod.TcpPing.return_value = True
515
    op = self.CopyOpCode(self.diskless_op)
516
    self.ExecOpCodeExpectOpPrereqError(
517
      op, "IP .* of instance diskless.test.com already in use")
518

    
519
  def testPrimaryIsSecondaryNode(self):
520
    op = self.CopyOpCode(self.drbd_op,
521
                         snode=self.drbd_op.pnode)
522
    self.ExecOpCodeExpectOpPrereqError(
523
      op, "The secondary node cannot be the primary node")
524

    
525
  def testPrimarySecondaryDifferentNodeGroups(self):
526
    group = self.cfg.AddNewNodeGroup()
527
    self.node2.group = group.uuid
528
    op = self.CopyOpCode(self.drbd_op)
529
    self.ExecOpCode(op)
530
    self.mcpu.assertLogContainsRegex(
531
      "The primary and secondary nodes are in two different node groups")
532

    
533
  def testExclusiveStorageUnsupportedDiskTemplate(self):
534
    self.node1.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
535
    op = self.CopyOpCode(self.drbd_op)
536
    self.ExecOpCodeExpectOpPrereqError(
537
      op, "Disk template drbd not supported with exclusive storage")
538

    
539
  def testAdoptPlain(self):
540
    self.rpc.call_lv_list.return_value = \
541
      self.RpcResultsBuilder() \
542
        .AddSuccessfulNode(self.master, {
543
          "xenvg/mock_disk_1": (10000, None, False)
544
        }) \
545
        .Build()
546
    op = self.CopyOpCode(self.plain_op)
547
    op.disks[0].update({constants.IDISK_ADOPT: "mock_disk_1"})
548
    self.ExecOpCode(op)
549

    
550
  def testAdoptPlainMissingLv(self):
551
    self.rpc.call_lv_list.return_value = \
552
      self.RpcResultsBuilder() \
553
        .AddSuccessfulNode(self.master, {}) \
554
        .Build()
555
    op = self.CopyOpCode(self.plain_op)
556
    op.disks[0].update({constants.IDISK_ADOPT: "mock_disk_1"})
557
    self.ExecOpCodeExpectOpPrereqError(op, "Missing logical volume")
558

    
559
  def testAdoptPlainOnlineLv(self):
560
    self.rpc.call_lv_list.return_value = \
561
      self.RpcResultsBuilder() \
562
        .AddSuccessfulNode(self.master, {
563
          "xenvg/mock_disk_1": (10000, None, True)
564
        }) \
565
        .Build()
566
    op = self.CopyOpCode(self.plain_op)
567
    op.disks[0].update({constants.IDISK_ADOPT: "mock_disk_1"})
568
    self.ExecOpCodeExpectOpPrereqError(
569
      op, "Online logical volumes found, cannot adopt")
570

    
571
  def testAdoptBlock(self):
572
    self.rpc.call_bdev_sizes.return_value = \
573
      self.RpcResultsBuilder() \
574
        .AddSuccessfulNode(self.master, {
575
          "/dev/disk/block0": 10000
576
        }) \
577
        .Build()
578
    op = self.CopyOpCode(self.block_op)
579
    self.ExecOpCode(op)
580

    
581
  def testAdoptBlockDuplicateNames(self):
582
    op = self.CopyOpCode(self.block_op,
583
                         disks=[{
584
                           constants.IDISK_SIZE: 0,
585
                           constants.IDISK_ADOPT: "/dev/disk/block0"
586
                         }, {
587
                           constants.IDISK_SIZE: 0,
588
                           constants.IDISK_ADOPT: "/dev/disk/block0"
589
                         }])
590
    self.ExecOpCodeExpectOpPrereqError(
591
      op, "Duplicate disk names given for adoption")
592

    
593
  def testAdoptBlockInvalidNames(self):
594
    op = self.CopyOpCode(self.block_op,
595
                         disks=[{
596
                           constants.IDISK_SIZE: 0,
597
                           constants.IDISK_ADOPT: "/invalid/block0"
598
                         }])
599
    self.ExecOpCodeExpectOpPrereqError(
600
      op, "Device node.* lie outside .* and cannot be adopted")
601

    
602
  def testAdoptBlockMissingDisk(self):
603
    self.rpc.call_bdev_sizes.return_value = \
604
      self.RpcResultsBuilder() \
605
        .AddSuccessfulNode(self.master, {}) \
606
        .Build()
607
    op = self.CopyOpCode(self.block_op)
608
    self.ExecOpCodeExpectOpPrereqError(op, "Missing block device")
609

    
610
  def testNoWaitForSyncDrbd(self):
611
    op = self.CopyOpCode(self.drbd_op,
612
                         wait_for_sync=False)
613
    self.ExecOpCode(op)
614

    
615
  def testNoWaitForSyncPlain(self):
616
    op = self.CopyOpCode(self.plain_op,
617
                         wait_for_sync=False)
618
    self.ExecOpCode(op)
619

    
620
  def testImportPlainFromGivenSrcNode(self):
621
    exp_info = """
622
[export]
623
version=0
624
os=mock_os
625
[instance]
626
name=old_name.example.com
627
"""
628

    
629
    self.rpc.call_export_info.return_value = \
630
      self.RpcResultsBuilder() \
631
        .CreateSuccessfulNodeResult(self.master, exp_info)
632
    op = self.CopyOpCode(self.plain_op,
633
                         mode=constants.INSTANCE_IMPORT,
634
                         src_node=self.master.name)
635
    self.ExecOpCode(op)
636

    
637
  def testImportPlainWithoutSrcNodeNotFound(self):
638
    op = self.CopyOpCode(self.plain_op,
639
                         mode=constants.INSTANCE_IMPORT)
640
    self.ExecOpCodeExpectOpPrereqError(
641
      op, "No export found for relative path")
642

    
643
  def testImportPlainWithoutSrcNode(self):
644
    exp_info = """
645
[export]
646
version=0
647
os=mock_os
648
[instance]
649
name=old_name.example.com
650
"""
651

    
652
    self.rpc.call_export_list.return_value = \
653
      self.RpcResultsBuilder() \
654
        .AddSuccessfulNode(self.master, {"mock_path": {}}) \
655
        .Build()
656
    self.rpc.call_export_info.return_value = \
657
      self.RpcResultsBuilder() \
658
        .CreateSuccessfulNodeResult(self.master, exp_info)
659

    
660
    op = self.CopyOpCode(self.plain_op,
661
                         mode=constants.INSTANCE_IMPORT,
662
                         src_path="mock_path")
663
    self.ExecOpCode(op)
664

    
665
  def testImportPlainCorruptExportInfo(self):
666
    exp_info = ""
667
    self.rpc.call_export_info.return_value = \
668
      self.RpcResultsBuilder() \
669
        .CreateSuccessfulNodeResult(self.master, exp_info)
670
    op = self.CopyOpCode(self.plain_op,
671
                         mode=constants.INSTANCE_IMPORT,
672
                         src_node=self.master.name)
673
    self.ExecOpCodeExpectException(op, errors.ProgrammerError,
674
                                   "Corrupted export config")
675

    
676
  def testImportPlainWrongExportInfoVersion(self):
677
    exp_info = """
678
[export]
679
version=1
680
"""
681
    self.rpc.call_export_info.return_value = \
682
      self.RpcResultsBuilder() \
683
        .CreateSuccessfulNodeResult(self.master, exp_info)
684
    op = self.CopyOpCode(self.plain_op,
685
                         mode=constants.INSTANCE_IMPORT,
686
                         src_node=self.master.name)
687
    self.ExecOpCodeExpectOpPrereqError(op, "Wrong export version")
688

    
689
  def testImportPlainWithParametersAndImport(self):
690
    exp_info = """
691
[export]
692
version=0
693
os=mock_os
694
[instance]
695
name=old_name.example.com
696
disk0_size=1024
697
disk1_size=1500
698
disk1_dump=mock_path
699
nic0_mode=bridged
700
nic0_link=br_mock
701
nic0_mac=f6:ab:f4:45:d1:af
702
nic0_ip=192.0.2.1
703
tags=tag1 tag2
704
hypervisor=xen-hvm
705
[hypervisor]
706
boot_order=cd
707
[backend]
708
memory=1024
709
vcpus=8
710
[os]
711
param1=val1
712
"""
713

    
714
    self.rpc.call_export_info.return_value = \
715
      self.RpcResultsBuilder() \
716
        .CreateSuccessfulNodeResult(self.master, exp_info)
717
    self.rpc.call_import_start.return_value = \
718
      self.RpcResultsBuilder() \
719
        .CreateSuccessfulNodeResult(self.master, "daemon_name")
720
    self.rpc.call_impexp_status.return_value = \
721
      self.RpcResultsBuilder() \
722
        .CreateSuccessfulNodeResult(self.master,
723
                                    [
724
                                      objects.ImportExportStatus(exit_status=0)
725
                                    ])
726
    self.rpc.call_impexp_cleanup.return_value = \
727
      self.RpcResultsBuilder() \
728
        .CreateSuccessfulNodeResult(self.master, True)
729

    
730
    op = self.CopyOpCode(self.plain_op,
731
                         disks=[],
732
                         nics=[],
733
                         tags=[],
734
                         hypervisor=None,
735
                         hvparams={},
736
                         mode=constants.INSTANCE_IMPORT,
737
                         src_node=self.master.name)
738
    self.ExecOpCode(op)
739

    
740

    
741
class TestCheckOSVariant(CmdlibTestCase):
742
  def testNoVariantsSupported(self):
743
    os = self.cfg.CreateOs(supported_variants=[])
744
    self.assertRaises(errors.OpPrereqError, instance._CheckOSVariant,
745
                      os, "os+variant")
746

    
747
  def testNoVariantGiven(self):
748
    os = self.cfg.CreateOs(supported_variants=["default"])
749
    self.assertRaises(errors.OpPrereqError, instance._CheckOSVariant,
750
                      os, "os")
751

    
752
  def testWrongVariantGiven(self):
753
    os = self.cfg.CreateOs(supported_variants=["default"])
754
    self.assertRaises(errors.OpPrereqError, instance._CheckOSVariant,
755
                      os, "os+wrong_variant")
756

    
757
  def testOkWithVariant(self):
758
    os = self.cfg.CreateOs(supported_variants=["default"])
759
    instance._CheckOSVariant(os, "os+default")
760

    
761
  def testOkWithoutVariant(self):
762
    os = self.cfg.CreateOs(supported_variants=[])
763
    instance._CheckOSVariant(os, "os")
764

    
765

    
766
class TestCheckTargetNodeIPolicy(TestLUInstanceCreate):
767
  def setUp(self):
768
    super(TestCheckTargetNodeIPolicy, self).setUp()
769

    
770
    self.op = self.diskless_op
771

    
772
    self.instance = self.cfg.AddNewInstance()
773
    self.target_group = self.cfg.AddNewNodeGroup()
774
    self.target_node = self.cfg.AddNewNode(group=self.target_group)
775

    
776
  @withLockedLU
777
  def testNoViolation(self, lu):
778
    compute_recoder = mock.Mock(return_value=[])
779
    instance.CheckTargetNodeIPolicy(lu, NotImplemented, self.instance,
780
                                    self.target_node, NotImplemented,
781
                                    _compute_fn=compute_recoder)
782
    self.assertTrue(compute_recoder.called)
783
    self.mcpu.assertLogIsEmpty()
784

    
785
  @withLockedLU
786
  def testNoIgnore(self, lu):
787
    compute_recoder = mock.Mock(return_value=["mem_size not in range"])
788
    self.assertRaises(errors.OpPrereqError, instance.CheckTargetNodeIPolicy,
789
                      lu, NotImplemented, self.instance,
790
                      self.target_node, NotImplemented,
791
                      _compute_fn=compute_recoder)
792
    self.assertTrue(compute_recoder.called)
793
    self.mcpu.assertLogIsEmpty()
794

    
795
  @withLockedLU
796
  def testIgnoreViolation(self, lu):
797
    compute_recoder = mock.Mock(return_value=["mem_size not in range"])
798
    instance.CheckTargetNodeIPolicy(lu, NotImplemented, self.instance,
799
                                    self.target_node, NotImplemented,
800
                                    ignore=True, _compute_fn=compute_recoder)
801
    self.assertTrue(compute_recoder.called)
802
    msg = ("Instance does not meet target node group's .* instance policy:"
803
           " mem_size not in range")
804
    self.mcpu.assertLogContainsRegex(msg)
805

    
806

    
807
class TestApplyContainerMods(unittest.TestCase):
808
  def testEmptyContainer(self):
809
    container = []
810
    chgdesc = []
811
    instance._ApplyContainerMods("test", container, chgdesc, [], None, None,
812
                                 None)
813
    self.assertEqual(container, [])
814
    self.assertEqual(chgdesc, [])
815

    
816
  def testAdd(self):
817
    container = []
818
    chgdesc = []
819
    mods = instance._PrepareContainerMods([
820
      (constants.DDM_ADD, -1, "Hello"),
821
      (constants.DDM_ADD, -1, "World"),
822
      (constants.DDM_ADD, 0, "Start"),
823
      (constants.DDM_ADD, -1, "End"),
824
      ], None)
825
    instance._ApplyContainerMods("test", container, chgdesc, mods,
826
                                 None, None, None)
827
    self.assertEqual(container, ["Start", "Hello", "World", "End"])
828
    self.assertEqual(chgdesc, [])
829

    
830
    mods = instance._PrepareContainerMods([
831
      (constants.DDM_ADD, 0, "zero"),
832
      (constants.DDM_ADD, 3, "Added"),
833
      (constants.DDM_ADD, 5, "four"),
834
      (constants.DDM_ADD, 7, "xyz"),
835
      ], None)
836
    instance._ApplyContainerMods("test", container, chgdesc, mods,
837
                                 None, None, None)
838
    self.assertEqual(container,
839
                     ["zero", "Start", "Hello", "Added", "World", "four",
840
                      "End", "xyz"])
841
    self.assertEqual(chgdesc, [])
842

    
843
    for idx in [-2, len(container) + 1]:
844
      mods = instance._PrepareContainerMods([
845
        (constants.DDM_ADD, idx, "error"),
846
        ], None)
847
      self.assertRaises(IndexError, instance._ApplyContainerMods,
848
                        "test", container, None, mods, None, None, None)
849

    
850
  def testRemoveError(self):
851
    for idx in [0, 1, 2, 100, -1, -4]:
852
      mods = instance._PrepareContainerMods([
853
        (constants.DDM_REMOVE, idx, None),
854
        ], None)
855
      self.assertRaises(IndexError, instance._ApplyContainerMods,
856
                        "test", [], None, mods, None, None, None)
857

    
858
    mods = instance._PrepareContainerMods([
859
      (constants.DDM_REMOVE, 0, object()),
860
      ], None)
861
    self.assertRaises(AssertionError, instance._ApplyContainerMods,
862
                      "test", [""], None, mods, None, None, None)
863

    
864
  def testAddError(self):
865
    for idx in range(-100, -1) + [100]:
866
      mods = instance._PrepareContainerMods([
867
        (constants.DDM_ADD, idx, None),
868
        ], None)
869
      self.assertRaises(IndexError, instance._ApplyContainerMods,
870
                        "test", [], None, mods, None, None, None)
871

    
872
  def testRemove(self):
873
    container = ["item 1", "item 2"]
874
    mods = instance._PrepareContainerMods([
875
      (constants.DDM_ADD, -1, "aaa"),
876
      (constants.DDM_REMOVE, -1, None),
877
      (constants.DDM_ADD, -1, "bbb"),
878
      ], None)
879
    chgdesc = []
880
    instance._ApplyContainerMods("test", container, chgdesc, mods,
881
                                 None, None, None)
882
    self.assertEqual(container, ["item 1", "item 2", "bbb"])
883
    self.assertEqual(chgdesc, [
884
      ("test/2", "remove"),
885
      ])
886

    
887
  def testModify(self):
888
    container = ["item 1", "item 2"]
889
    mods = instance._PrepareContainerMods([
890
      (constants.DDM_MODIFY, -1, "a"),
891
      (constants.DDM_MODIFY, 0, "b"),
892
      (constants.DDM_MODIFY, 1, "c"),
893
      ], None)
894
    chgdesc = []
895
    instance._ApplyContainerMods("test", container, chgdesc, mods,
896
                                 None, None, None)
897
    self.assertEqual(container, ["item 1", "item 2"])
898
    self.assertEqual(chgdesc, [])
899

    
900
    for idx in [-2, len(container) + 1]:
901
      mods = instance._PrepareContainerMods([
902
        (constants.DDM_MODIFY, idx, "error"),
903
        ], None)
904
      self.assertRaises(IndexError, instance._ApplyContainerMods,
905
                        "test", container, None, mods, None, None, None)
906

    
907
  @staticmethod
908
  def _CreateTestFn(idx, params, private):
909
    private.data = ("add", idx, params)
910
    return ((100 * idx, params), [
911
      ("test/%s" % idx, hex(idx)),
912
      ])
913

    
914
  @staticmethod
915
  def _ModifyTestFn(idx, item, params, private):
916
    private.data = ("modify", idx, params)
917
    return [
918
      ("test/%s" % idx, "modify %s" % params),
919
      ]
920

    
921
  @staticmethod
922
  def _RemoveTestFn(idx, item, private):
923
    private.data = ("remove", idx, item)
924

    
925
  def testAddWithCreateFunction(self):
926
    container = []
927
    chgdesc = []
928
    mods = instance._PrepareContainerMods([
929
      (constants.DDM_ADD, -1, "Hello"),
930
      (constants.DDM_ADD, -1, "World"),
931
      (constants.DDM_ADD, 0, "Start"),
932
      (constants.DDM_ADD, -1, "End"),
933
      (constants.DDM_REMOVE, 2, None),
934
      (constants.DDM_MODIFY, -1, "foobar"),
935
      (constants.DDM_REMOVE, 2, None),
936
      (constants.DDM_ADD, 1, "More"),
937
      ], mock.Mock)
938
    instance._ApplyContainerMods("test", container, chgdesc, mods,
939
                                 self._CreateTestFn, self._ModifyTestFn,
940
                                 self._RemoveTestFn)
941
    self.assertEqual(container, [
942
      (000, "Start"),
943
      (100, "More"),
944
      (000, "Hello"),
945
      ])
946
    self.assertEqual(chgdesc, [
947
      ("test/0", "0x0"),
948
      ("test/1", "0x1"),
949
      ("test/0", "0x0"),
950
      ("test/3", "0x3"),
951
      ("test/2", "remove"),
952
      ("test/2", "modify foobar"),
953
      ("test/2", "remove"),
954
      ("test/1", "0x1")
955
      ])
956
    self.assertTrue(compat.all(op == private.data[0]
957
                               for (op, _, _, private) in mods))
958
    self.assertEqual([private.data for (op, _, _, private) in mods], [
959
      ("add", 0, "Hello"),
960
      ("add", 1, "World"),
961
      ("add", 0, "Start"),
962
      ("add", 3, "End"),
963
      ("remove", 2, (100, "World")),
964
      ("modify", 2, "foobar"),
965
      ("remove", 2, (300, "End")),
966
      ("add", 1, "More"),
967
      ])
968

    
969

    
970
class _FakeConfigForGenDiskTemplate(ConfigMock):
971
  def __init__(self):
972
    super(_FakeConfigForGenDiskTemplate, self).__init__()
973

    
974
    self._unique_id = itertools.count()
975
    self._drbd_minor = itertools.count(20)
976
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
977
    self._secret = itertools.count()
978

    
979
  def GenerateUniqueID(self, ec_id):
980
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
981

    
982
  def AllocateDRBDMinor(self, nodes, instance):
983
    return [self._drbd_minor.next()
984
            for _ in nodes]
985

    
986
  def AllocatePort(self):
987
    return self._port.next()
988

    
989
  def GenerateDRBDSecret(self, ec_id):
990
    return "ec%s-secret%s" % (ec_id, self._secret.next())
991

    
992

    
993
class TestGenerateDiskTemplate(CmdlibTestCase):
994
  def setUp(self):
995
    super(TestGenerateDiskTemplate, self).setUp()
996

    
997
    self.cfg = _FakeConfigForGenDiskTemplate()
998
    self.cluster.enabled_disk_templates = list(constants.DISK_TEMPLATES)
999

    
1000
    self.nodegroup = self.cfg.AddNewNodeGroup(name="ng")
1001

    
1002
    self.lu = self.GetMockLU()
1003

    
1004
  @staticmethod
1005
  def GetDiskParams():
1006
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1007

    
1008
  def testWrongDiskTemplate(self):
1009
    gdt = instance.GenerateDiskTemplate
1010
    disk_template = "##unknown##"
1011

    
1012
    assert disk_template not in constants.DISK_TEMPLATES
1013

    
1014
    self.assertRaises(errors.OpPrereqError, gdt, self.lu, disk_template,
1015
                      "inst26831.example.com", "node30113.example.com", [], [],
1016
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1017
                      self.GetDiskParams())
1018

    
1019
  def testDiskless(self):
1020
    gdt = instance.GenerateDiskTemplate
1021

    
1022
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1023
                 "node30113.example.com", [], [],
1024
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1025
                 self.GetDiskParams())
1026
    self.assertEqual(result, [])
1027

    
1028
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1029
                       file_storage_dir=NotImplemented,
1030
                       file_driver=NotImplemented):
1031
    gdt = instance.GenerateDiskTemplate
1032

    
1033
    map(lambda params: utils.ForceDictType(params,
1034
                                           constants.IDISK_PARAMS_TYPES),
1035
        disk_info)
1036

    
1037
    # Check if non-empty list of secondaries is rejected
1038
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1039
                      template, "inst25088.example.com",
1040
                      "node185.example.com", ["node323.example.com"], [],
1041
                      NotImplemented, NotImplemented, base_index,
1042
                      self.lu.LogInfo, self.GetDiskParams())
1043

    
1044
    result = gdt(self.lu, template, "inst21662.example.com",
1045
                 "node21741.example.com", [],
1046
                 disk_info, file_storage_dir, file_driver, base_index,
1047
                 self.lu.LogInfo, self.GetDiskParams())
1048

    
1049
    for (idx, disk) in enumerate(result):
1050
      self.assertTrue(isinstance(disk, objects.Disk))
1051
      self.assertEqual(disk.dev_type, exp_dev_type)
1052
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1053
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1054
      self.assertTrue(disk.children is None)
1055

    
1056
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1057
    instance._UpdateIvNames(base_index, result)
1058
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1059

    
1060
    return result
1061

    
1062
  def _CheckIvNames(self, disks, base_index, end_index):
1063
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1064
                     ["disk/%s" % i for i in range(base_index, end_index)])
1065

    
1066
  def testPlain(self):
1067
    disk_info = [{
1068
      constants.IDISK_SIZE: 1024,
1069
      constants.IDISK_MODE: constants.DISK_RDWR,
1070
      }, {
1071
      constants.IDISK_SIZE: 4096,
1072
      constants.IDISK_VG: "othervg",
1073
      constants.IDISK_MODE: constants.DISK_RDWR,
1074
      }]
1075

    
1076
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1077
                                   constants.DT_PLAIN)
1078

    
1079
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1080
      ("xenvg", "ec1-uq0.disk3"),
1081
      ("othervg", "ec1-uq1.disk4"),
1082
      ])
1083

    
1084
  def testFile(self):
1085
    # anything != DT_FILE would do here
1086
    self.cluster.enabled_disk_templates = [constants.DT_PLAIN]
1087
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1088
                      constants.DT_FILE, [], 0, NotImplemented)
1089
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1090
                      constants.DT_SHARED_FILE, [], 0, NotImplemented)
1091

    
1092
    for disk_template in constants.DTS_FILEBASED:
1093
      disk_info = [{
1094
        constants.IDISK_SIZE: 80 * 1024,
1095
        constants.IDISK_MODE: constants.DISK_RDONLY,
1096
        }, {
1097
        constants.IDISK_SIZE: 4096,
1098
        constants.IDISK_MODE: constants.DISK_RDWR,
1099
        }, {
1100
        constants.IDISK_SIZE: 6 * 1024,
1101
        constants.IDISK_MODE: constants.DISK_RDWR,
1102
        }]
1103

    
1104
      self.cluster.enabled_disk_templates = [disk_template]
1105
      result = self._TestTrivialDisk(
1106
        disk_template, disk_info, 2, disk_template,
1107
        file_storage_dir="/tmp", file_driver=constants.FD_BLKTAP)
1108

    
1109
      if disk_template == constants.DT_GLUSTER:
1110
        # Here "inst21662.example.com" is actually the instance UUID, not its
1111
        # name, so while this result looks wrong, it is actually correct.
1112
        expected = [(constants.FD_BLKTAP,
1113
                     'ganeti/inst21662.example.com.%d' % x)
1114
                    for x in (2,3,4)]
1115
      else:
1116
        expected = [(constants.FD_BLKTAP,
1117
                     '/tmp/disk%d' % x)
1118
                    for x in (2,3,4)]
1119

    
1120
      self.assertEqual(map(operator.attrgetter("logical_id"), result),
1121
                       expected)
1122

    
1123
  def testBlock(self):
1124
    disk_info = [{
1125
      constants.IDISK_SIZE: 8 * 1024,
1126
      constants.IDISK_MODE: constants.DISK_RDWR,
1127
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1128
      }]
1129

    
1130
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1131
                                   constants.DT_BLOCK)
1132

    
1133
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1134
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1135
      ])
1136

    
1137
  def testRbd(self):
1138
    disk_info = [{
1139
      constants.IDISK_SIZE: 8 * 1024,
1140
      constants.IDISK_MODE: constants.DISK_RDONLY,
1141
      }, {
1142
      constants.IDISK_SIZE: 100 * 1024,
1143
      constants.IDISK_MODE: constants.DISK_RDWR,
1144
      }]
1145

    
1146
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1147
                                   constants.DT_RBD)
1148

    
1149
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1150
      ("rbd", "ec1-uq0.rbd.disk0"),
1151
      ("rbd", "ec1-uq1.rbd.disk1"),
1152
      ])
1153

    
1154
  def testDrbd8(self):
1155
    gdt = instance.GenerateDiskTemplate
1156
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.DT_DRBD8]
1157
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1158

    
1159
    disk_info = [{
1160
      constants.IDISK_SIZE: 1024,
1161
      constants.IDISK_MODE: constants.DISK_RDWR,
1162
      }, {
1163
      constants.IDISK_SIZE: 100 * 1024,
1164
      constants.IDISK_MODE: constants.DISK_RDONLY,
1165
      constants.IDISK_METAVG: "metavg",
1166
      }, {
1167
      constants.IDISK_SIZE: 4096,
1168
      constants.IDISK_MODE: constants.DISK_RDWR,
1169
      constants.IDISK_VG: "vgxyz",
1170
      },
1171
      ]
1172

    
1173
    exp_logical_ids = [
1174
      [
1175
        (self.lu.cfg.GetVGName(), "ec1-uq0.disk0_data"),
1176
        (drbd8_default_metavg, "ec1-uq0.disk0_meta"),
1177
      ], [
1178
        (self.lu.cfg.GetVGName(), "ec1-uq1.disk1_data"),
1179
        ("metavg", "ec1-uq1.disk1_meta"),
1180
      ], [
1181
        ("vgxyz", "ec1-uq2.disk2_data"),
1182
        (drbd8_default_metavg, "ec1-uq2.disk2_meta"),
1183
      ]]
1184

    
1185
    assert len(exp_logical_ids) == len(disk_info)
1186

    
1187
    map(lambda params: utils.ForceDictType(params,
1188
                                           constants.IDISK_PARAMS_TYPES),
1189
        disk_info)
1190

    
1191
    # Check if empty list of secondaries is rejected
1192
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1193
                      "inst827.example.com", "node1334.example.com", [],
1194
                      disk_info, NotImplemented, NotImplemented, 0,
1195
                      self.lu.LogInfo, self.GetDiskParams())
1196

    
1197
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1198
                 "node1334.example.com", ["node12272.example.com"],
1199
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1200
                 self.GetDiskParams())
1201

    
1202
    for (idx, disk) in enumerate(result):
1203
      self.assertTrue(isinstance(disk, objects.Disk))
1204
      self.assertEqual(disk.dev_type, constants.DT_DRBD8)
1205
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1206
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1207

    
1208
      for child in disk.children:
1209
        self.assertTrue(isinstance(disk, objects.Disk))
1210
        self.assertEqual(child.dev_type, constants.DT_PLAIN)
1211
        self.assertTrue(child.children is None)
1212

    
1213
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1214
                       exp_logical_ids[idx])
1215

    
1216
      self.assertEqual(len(disk.children), 2)
1217
      self.assertEqual(disk.children[0].size, disk.size)
1218
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1219

    
1220
    self._CheckIvNames(result, 0, len(disk_info))
1221
    instance._UpdateIvNames(0, result)
1222
    self._CheckIvNames(result, 0, len(disk_info))
1223

    
1224
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1225
      ("node1334.example.com", "node12272.example.com",
1226
       constants.FIRST_DRBD_PORT, 20, 21, "ec1-secret0"),
1227
      ("node1334.example.com", "node12272.example.com",
1228
       constants.FIRST_DRBD_PORT + 1, 22, 23, "ec1-secret1"),
1229
      ("node1334.example.com", "node12272.example.com",
1230
       constants.FIRST_DRBD_PORT + 2, 24, 25, "ec1-secret2"),
1231
      ])
1232

    
1233

    
1234
class _DiskPauseTracker:
1235
  def __init__(self):
1236
    self.history = []
1237

    
1238
  def __call__(self, (disks, instance), pause):
1239
    assert not (set(disks) - set(instance.disks))
1240

    
1241
    self.history.extend((i.logical_id, i.size, pause)
1242
                        for i in disks)
1243

    
1244
    return (True, [True] * len(disks))
1245

    
1246

    
1247
class _ConfigForDiskWipe:
1248
  def __init__(self, exp_node_uuid):
1249
    self._exp_node_uuid = exp_node_uuid
1250

    
1251
  def GetNodeName(self, node_uuid):
1252
    assert node_uuid == self._exp_node_uuid
1253
    return "name.of.expected.node"
1254

    
1255

    
1256
class _RpcForDiskWipe:
1257
  def __init__(self, exp_node, pause_cb, wipe_cb):
1258
    self._exp_node = exp_node
1259
    self._pause_cb = pause_cb
1260
    self._wipe_cb = wipe_cb
1261

    
1262
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1263
    assert node == self._exp_node
1264
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1265

    
1266
  def call_blockdev_wipe(self, node, bdev, offset, size):
1267
    assert node == self._exp_node
1268
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1269

    
1270

    
1271
class _DiskWipeProgressTracker:
1272
  def __init__(self, start_offset):
1273
    self._start_offset = start_offset
1274
    self.progress = {}
1275

    
1276
  def __call__(self, (disk, _), offset, size):
1277
    assert isinstance(offset, (long, int))
1278
    assert isinstance(size, (long, int))
1279

    
1280
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1281

    
1282
    assert offset >= self._start_offset
1283
    assert (offset + size) <= disk.size
1284

    
1285
    assert size > 0
1286
    assert size <= constants.MAX_WIPE_CHUNK
1287
    assert size <= max_chunk_size
1288

    
1289
    assert offset == self._start_offset or disk.logical_id in self.progress
1290

    
1291
    # Keep track of progress
1292
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1293

    
1294
    assert cur_progress == offset
1295

    
1296
    # Record progress
1297
    self.progress[disk.logical_id] += size
1298

    
1299
    return (True, None)
1300

    
1301

    
1302
class TestWipeDisks(unittest.TestCase):
1303
  def _FailingPauseCb(self, (disks, _), pause):
1304
    self.assertEqual(len(disks), 3)
1305
    self.assertTrue(pause)
1306
    # Simulate an RPC error
1307
    return (False, "error")
1308

    
1309
  def testPauseFailure(self):
1310
    node_name = "node1372.example.com"
1311

    
1312
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1313
                                     NotImplemented),
1314
                 cfg=_ConfigForDiskWipe(node_name))
1315

    
1316
    disks = [
1317
      objects.Disk(dev_type=constants.DT_PLAIN),
1318
      objects.Disk(dev_type=constants.DT_PLAIN),
1319
      objects.Disk(dev_type=constants.DT_PLAIN),
1320
      ]
1321

    
1322
    inst = objects.Instance(name="inst21201",
1323
                            primary_node=node_name,
1324
                            disk_template=constants.DT_PLAIN,
1325
                            disks=disks)
1326

    
1327
    self.assertRaises(errors.OpExecError, instance.WipeDisks, lu, inst)
1328

    
1329
  def _FailingWipeCb(self, (disk, _), offset, size):
1330
    # This should only ever be called for the first disk
1331
    self.assertEqual(disk.logical_id, "disk0")
1332
    return (False, None)
1333

    
1334
  def testFailingWipe(self):
1335
    node_uuid = "node13445-uuid"
1336
    pt = _DiskPauseTracker()
1337

    
1338
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_uuid, pt, self._FailingWipeCb),
1339
                 cfg=_ConfigForDiskWipe(node_uuid))
1340

    
1341
    disks = [
1342
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0",
1343
                   size=100 * 1024),
1344
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1345
                   size=500 * 1024),
1346
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk2", size=256),
1347
      ]
1348

    
1349
    inst = objects.Instance(name="inst562",
1350
                            primary_node=node_uuid,
1351
                            disk_template=constants.DT_PLAIN,
1352
                            disks=disks)
1353

    
1354
    try:
1355
      instance.WipeDisks(lu, inst)
1356
    except errors.OpExecError, err:
1357
      self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1358
    else:
1359
      self.fail("Did not raise exception")
1360

    
1361
    # Check if all disks were paused and resumed
1362
    self.assertEqual(pt.history, [
1363
      ("disk0", 100 * 1024, True),
1364
      ("disk1", 500 * 1024, True),
1365
      ("disk2", 256, True),
1366
      ("disk0", 100 * 1024, False),
1367
      ("disk1", 500 * 1024, False),
1368
      ("disk2", 256, False),
1369
      ])
1370

    
1371
  def _PrepareWipeTest(self, start_offset, disks):
1372
    node_name = "node-with-offset%s.example.com" % start_offset
1373
    pauset = _DiskPauseTracker()
1374
    progresst = _DiskWipeProgressTracker(start_offset)
1375

    
1376
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1377
                 cfg=_ConfigForDiskWipe(node_name))
1378

    
1379
    instance = objects.Instance(name="inst3560",
1380
                                primary_node=node_name,
1381
                                disk_template=constants.DT_PLAIN,
1382
                                disks=disks)
1383

    
1384
    return (lu, instance, pauset, progresst)
1385

    
1386
  def testNormalWipe(self):
1387
    disks = [
1388
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0", size=1024),
1389
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1390
                   size=500 * 1024),
1391
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk2", size=128),
1392
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk3",
1393
                   size=constants.MAX_WIPE_CHUNK),
1394
      ]
1395

    
1396
    (lu, inst, pauset, progresst) = self._PrepareWipeTest(0, disks)
1397

    
1398
    instance.WipeDisks(lu, inst)
1399

    
1400
    self.assertEqual(pauset.history, [
1401
      ("disk0", 1024, True),
1402
      ("disk1", 500 * 1024, True),
1403
      ("disk2", 128, True),
1404
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1405
      ("disk0", 1024, False),
1406
      ("disk1", 500 * 1024, False),
1407
      ("disk2", 128, False),
1408
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1409
      ])
1410

    
1411
    # Ensure the complete disk has been wiped
1412
    self.assertEqual(progresst.progress,
1413
                     dict((i.logical_id, i.size) for i in disks))
1414

    
1415
  def testWipeWithStartOffset(self):
1416
    for start_offset in [0, 280, 8895, 1563204]:
1417
      disks = [
1418
        objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0",
1419
                     size=128),
1420
        objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1421
                     size=start_offset + (100 * 1024)),
1422
        ]
1423

    
1424
      (lu, inst, pauset, progresst) = \
1425
        self._PrepareWipeTest(start_offset, disks)
1426

    
1427
      # Test start offset with only one disk
1428
      instance.WipeDisks(lu, inst,
1429
                         disks=[(1, disks[1], start_offset)])
1430

    
1431
      # Only the second disk may have been paused and wiped
1432
      self.assertEqual(pauset.history, [
1433
        ("disk1", start_offset + (100 * 1024), True),
1434
        ("disk1", start_offset + (100 * 1024), False),
1435
        ])
1436
      self.assertEqual(progresst.progress, {
1437
        "disk1": disks[1].size,
1438
        })
1439

    
1440

    
1441
class TestCheckOpportunisticLocking(unittest.TestCase):
1442
  class OpTest(opcodes.OpCode):
1443
    OP_PARAMS = [
1444
      ("opportunistic_locking", False, ht.TBool, None),
1445
      ("iallocator", None, ht.TMaybe(ht.TNonEmptyString), "")
1446
      ]
1447

    
1448
  @classmethod
1449
  def _MakeOp(cls, **kwargs):
1450
    op = cls.OpTest(**kwargs)
1451
    op.Validate(True)
1452
    return op
1453

    
1454
  def testMissingAttributes(self):
1455
    self.assertRaises(AttributeError, instance._CheckOpportunisticLocking,
1456
                      object())
1457

    
1458
  def testDefaults(self):
1459
    op = self._MakeOp()
1460
    instance._CheckOpportunisticLocking(op)
1461

    
1462
  def test(self):
1463
    for iallocator in [None, "something", "other"]:
1464
      for opplock in [False, True]:
1465
        op = self._MakeOp(iallocator=iallocator,
1466
                          opportunistic_locking=opplock)
1467
        if opplock and not iallocator:
1468
          self.assertRaises(errors.OpPrereqError,
1469
                            instance._CheckOpportunisticLocking, op)
1470
        else:
1471
          instance._CheckOpportunisticLocking(op)
1472

    
1473

    
1474
class TestLUInstanceRemove(CmdlibTestCase):
1475
  def testRemoveMissingInstance(self):
1476
    op = opcodes.OpInstanceRemove(instance_name="missing.inst")
1477
    self.ExecOpCodeExpectOpPrereqError(op, "Instance 'missing.inst' not known")
1478

    
1479
  def testRemoveInst(self):
1480
    inst = self.cfg.AddNewInstance(disks=[])
1481
    op = opcodes.OpInstanceRemove(instance_name=inst.name)
1482
    self.ExecOpCode(op)
1483

    
1484

    
1485
class TestLUInstanceMove(CmdlibTestCase):
1486
  def setUp(self):
1487
    super(TestLUInstanceMove, self).setUp()
1488

    
1489
    self.node = self.cfg.AddNewNode()
1490

    
1491
    self.rpc.call_blockdev_assemble.return_value = \
1492
      self.RpcResultsBuilder() \
1493
        .CreateSuccessfulNodeResult(self.node, ("/dev/mocked_path",
1494
                                    "/var/run/ganeti/instance-disks/mocked_d"))
1495
    self.rpc.call_blockdev_remove.return_value = \
1496
      self.RpcResultsBuilder() \
1497
        .CreateSuccessfulNodeResult(self.master, "")
1498

    
1499
    def ImportStart(node_uuid, opt, inst, component, args):
1500
      return self.RpcResultsBuilder() \
1501
               .CreateSuccessfulNodeResult(node_uuid,
1502
                                           "deamon_on_%s" % node_uuid)
1503
    self.rpc.call_import_start.side_effect = ImportStart
1504

    
1505
    def ImpExpStatus(node_uuid, name):
1506
      return self.RpcResultsBuilder() \
1507
               .CreateSuccessfulNodeResult(node_uuid,
1508
                                           [objects.ImportExportStatus(
1509
                                             exit_status=0
1510
                                           )])
1511
    self.rpc.call_impexp_status.side_effect = ImpExpStatus
1512

    
1513
    def ImpExpCleanup(node_uuid, name):
1514
      return self.RpcResultsBuilder() \
1515
               .CreateSuccessfulNodeResult(node_uuid)
1516
    self.rpc.call_impexp_cleanup.side_effect = ImpExpCleanup
1517

    
1518
  def testMissingInstance(self):
1519
    op = opcodes.OpInstanceMove(instance_name="missing.inst",
1520
                                target_node=self.node.name)
1521
    self.ExecOpCodeExpectOpPrereqError(op, "Instance 'missing.inst' not known")
1522

    
1523
  def testUncopyableDiskTemplate(self):
1524
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_SHARED_FILE)
1525
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1526
                                target_node=self.node.name)
1527
    self.ExecOpCodeExpectOpPrereqError(
1528
      op, "Disk template sharedfile not suitable for copying")
1529

    
1530
  def testAlreadyOnTargetNode(self):
1531
    inst = self.cfg.AddNewInstance()
1532
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1533
                                target_node=self.master.name)
1534
    self.ExecOpCodeExpectOpPrereqError(
1535
      op, "Instance .* is already on the node .*")
1536

    
1537
  def testMoveStoppedInstance(self):
1538
    inst = self.cfg.AddNewInstance()
1539
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1540
                                target_node=self.node.name)
1541
    self.ExecOpCode(op)
1542

    
1543
  def testMoveRunningInstance(self):
1544
    self.rpc.call_node_info.return_value = \
1545
      self.RpcResultsBuilder() \
1546
        .AddSuccessfulNode(self.node,
1547
                           (NotImplemented, NotImplemented,
1548
                            ({"memory_free": 10000}, ))) \
1549
        .Build()
1550
    self.rpc.call_instance_start.return_value = \
1551
      self.RpcResultsBuilder() \
1552
        .CreateSuccessfulNodeResult(self.node, "")
1553

    
1554
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1555
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1556
                                target_node=self.node.name)
1557
    self.ExecOpCode(op)
1558

    
1559
  def testMoveFailingStartInstance(self):
1560
    self.rpc.call_node_info.return_value = \
1561
      self.RpcResultsBuilder() \
1562
        .AddSuccessfulNode(self.node,
1563
                           (NotImplemented, NotImplemented,
1564
                            ({"memory_free": 10000}, ))) \
1565
        .Build()
1566
    self.rpc.call_instance_start.return_value = \
1567
      self.RpcResultsBuilder() \
1568
        .CreateFailedNodeResult(self.node)
1569

    
1570
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1571
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1572
                                target_node=self.node.name)
1573
    self.ExecOpCodeExpectOpExecError(
1574
      op, "Could not start instance .* on node .*")
1575

    
1576
  def testMoveFailingImpExpDaemonExitCode(self):
1577
    inst = self.cfg.AddNewInstance()
1578
    self.rpc.call_impexp_status.side_effect = None
1579
    self.rpc.call_impexp_status.return_value = \
1580
      self.RpcResultsBuilder() \
1581
        .CreateSuccessfulNodeResult(self.node,
1582
                                    [objects.ImportExportStatus(
1583
                                      exit_status=1,
1584
                                      recent_output=["mock output"]
1585
                                    )])
1586
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1587
                                target_node=self.node.name)
1588
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1589

    
1590
  def testMoveFailingStartImpExpDaemon(self):
1591
    inst = self.cfg.AddNewInstance()
1592
    self.rpc.call_import_start.side_effect = None
1593
    self.rpc.call_import_start.return_value = \
1594
      self.RpcResultsBuilder() \
1595
        .CreateFailedNodeResult(self.node)
1596
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1597
                                target_node=self.node.name)
1598
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1599

    
1600

    
1601
class TestLUInstanceRename(CmdlibTestCase):
1602
  def setUp(self):
1603
    super(TestLUInstanceRename, self).setUp()
1604

    
1605
    self.inst = self.cfg.AddNewInstance()
1606

    
1607
    self.op = opcodes.OpInstanceRename(instance_name=self.inst.name,
1608
                                       new_name="new_name.example.com")
1609

    
1610
  def testIpCheckWithoutNameCheck(self):
1611
    op = self.CopyOpCode(self.op,
1612
                         ip_check=True,
1613
                         name_check=False)
1614
    self.ExecOpCodeExpectOpPrereqError(
1615
      op, "IP address check requires a name check")
1616

    
1617
  def testIpAlreadyInUse(self):
1618
    self.netutils_mod.TcpPing.return_value = True
1619
    op = self.CopyOpCode(self.op)
1620
    self.ExecOpCodeExpectOpPrereqError(
1621
      op, "IP .* of instance .* already in use")
1622

    
1623
  def testExistingInstanceName(self):
1624
    self.cfg.AddNewInstance(name="new_name.example.com")
1625
    op = self.CopyOpCode(self.op)
1626
    self.ExecOpCodeExpectOpPrereqError(
1627
      op, "Instance .* is already in the cluster")
1628

    
1629
  def testFileInstance(self):
1630
    self.rpc.call_blockdev_assemble.return_value = \
1631
      self.RpcResultsBuilder() \
1632
        .CreateSuccessfulNodeResult(self.master, (None, None))
1633
    self.rpc.call_blockdev_shutdown.return_value = \
1634
      self.RpcResultsBuilder() \
1635
        .CreateSuccessfulNodeResult(self.master, (None, None))
1636

    
1637
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_FILE)
1638
    op = self.CopyOpCode(self.op,
1639
                         instance_name=inst.name)
1640
    self.ExecOpCode(op)
1641

    
1642

    
1643
class TestLUInstanceMultiAlloc(CmdlibTestCase):
1644
  def setUp(self):
1645
    super(TestLUInstanceMultiAlloc, self).setUp()
1646

    
1647
    self.inst_op = opcodes.OpInstanceCreate(instance_name="inst.example.com",
1648
                                            disk_template=constants.DT_DRBD8,
1649
                                            disks=[],
1650
                                            nics=[],
1651
                                            os_type="mock_os",
1652
                                            hypervisor=constants.HT_XEN_HVM,
1653
                                            mode=constants.INSTANCE_CREATE)
1654

    
1655
  def testInstanceWithIAllocator(self):
1656
    inst = self.CopyOpCode(self.inst_op,
1657
                           iallocator="mock")
1658
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1659
    self.ExecOpCodeExpectOpPrereqError(
1660
      op, "iallocator are not allowed to be set on instance objects")
1661

    
1662
  def testOnlySomeNodesGiven(self):
1663
    inst1 = self.CopyOpCode(self.inst_op,
1664
                            pnode=self.master.name)
1665
    inst2 = self.CopyOpCode(self.inst_op)
1666
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1667
    self.ExecOpCodeExpectOpPrereqError(
1668
      op, "There are instance objects providing pnode/snode while others"
1669
          " do not")
1670

    
1671
  def testMissingIAllocator(self):
1672
    self.cluster.default_iallocator = None
1673
    inst = self.CopyOpCode(self.inst_op)
1674
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1675
    self.ExecOpCodeExpectOpPrereqError(
1676
      op, "No iallocator or nodes on the instances given and no cluster-wide"
1677
          " default iallocator found")
1678

    
1679
  def testDuplicateInstanceNames(self):
1680
    inst1 = self.CopyOpCode(self.inst_op)
1681
    inst2 = self.CopyOpCode(self.inst_op)
1682
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1683
    self.ExecOpCodeExpectOpPrereqError(
1684
      op, "There are duplicate instance names")
1685

    
1686
  def testWithGivenNodes(self):
1687
    snode = self.cfg.AddNewNode()
1688
    inst = self.CopyOpCode(self.inst_op,
1689
                           pnode=self.master.name,
1690
                           snode=snode.name)
1691
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1692
    self.ExecOpCode(op)
1693

    
1694
  def testDryRun(self):
1695
    snode = self.cfg.AddNewNode()
1696
    inst = self.CopyOpCode(self.inst_op,
1697
                           pnode=self.master.name,
1698
                           snode=snode.name)
1699
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1700
                                      dry_run=True)
1701
    self.ExecOpCode(op)
1702

    
1703
  def testWithIAllocator(self):
1704
    snode = self.cfg.AddNewNode()
1705
    self.iallocator_cls.return_value.result = \
1706
      ([("inst.example.com", [self.master.name, snode.name])], [])
1707

    
1708
    inst = self.CopyOpCode(self.inst_op)
1709
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1710
                                      iallocator="mock_ialloc")
1711
    self.ExecOpCode(op)
1712

    
1713
  def testWithIAllocatorOpportunisticLocking(self):
1714
    snode = self.cfg.AddNewNode()
1715
    self.iallocator_cls.return_value.result = \
1716
      ([("inst.example.com", [self.master.name, snode.name])], [])
1717

    
1718
    inst = self.CopyOpCode(self.inst_op)
1719
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1720
                                      iallocator="mock_ialloc",
1721
                                      opportunistic_locking=True)
1722
    self.ExecOpCode(op)
1723

    
1724
  def testFailingIAllocator(self):
1725
    self.iallocator_cls.return_value.success = False
1726

    
1727
    inst = self.CopyOpCode(self.inst_op)
1728
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1729
                                      iallocator="mock_ialloc")
1730
    self.ExecOpCodeExpectOpPrereqError(
1731
      op, "Can't compute nodes using iallocator")
1732

    
1733

    
1734
class TestLUInstanceSetParams(CmdlibTestCase):
1735
  def setUp(self):
1736
    super(TestLUInstanceSetParams, self).setUp()
1737

    
1738
    self.inst = self.cfg.AddNewInstance()
1739
    self.op = opcodes.OpInstanceSetParams(instance_name=self.inst.name)
1740

    
1741
    self.running_inst = \
1742
      self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1743
    self.running_op = \
1744
      opcodes.OpInstanceSetParams(instance_name=self.running_inst.name)
1745

    
1746
    self.snode = self.cfg.AddNewNode()
1747

    
1748
    self.mocked_storage_type = constants.ST_LVM_VG
1749
    self.mocked_storage_free = 10000
1750
    self.mocked_master_cpu_total = 16
1751
    self.mocked_master_memory_free = 2048
1752
    self.mocked_snode_cpu_total = 16
1753
    self.mocked_snode_memory_free = 512
1754

    
1755
    self.mocked_running_inst_memory = 1024
1756
    self.mocked_running_inst_vcpus = 8
1757
    self.mocked_running_inst_state = "running"
1758
    self.mocked_running_inst_time = 10938474
1759

    
1760
    bootid = "mock_bootid"
1761
    storage_info = [
1762
      {
1763
        "type": self.mocked_storage_type,
1764
        "storage_free": self.mocked_storage_free
1765
      }
1766
    ]
1767
    hv_info_master = {
1768
      "cpu_total": self.mocked_master_cpu_total,
1769
      "memory_free": self.mocked_master_memory_free
1770
    }
1771
    hv_info_snode = {
1772
      "cpu_total": self.mocked_snode_cpu_total,
1773
      "memory_free": self.mocked_snode_memory_free
1774
    }
1775

    
1776
    self.rpc.call_node_info.return_value = \
1777
      self.RpcResultsBuilder() \
1778
        .AddSuccessfulNode(self.master,
1779
                           (bootid, storage_info, (hv_info_master, ))) \
1780
        .AddSuccessfulNode(self.snode,
1781
                           (bootid, storage_info, (hv_info_snode, ))) \
1782
        .Build()
1783

    
1784
    def _InstanceInfo(_, instance, __, ___):
1785
      if instance == self.inst.name:
1786
        return self.RpcResultsBuilder() \
1787
          .CreateSuccessfulNodeResult(self.master, None)
1788
      elif instance == self.running_inst.name:
1789
        return self.RpcResultsBuilder() \
1790
          .CreateSuccessfulNodeResult(
1791
            self.master, {
1792
              "memory": self.mocked_running_inst_memory,
1793
              "vcpus": self.mocked_running_inst_vcpus,
1794
              "state": self.mocked_running_inst_state,
1795
              "time": self.mocked_running_inst_time
1796
            })
1797
      else:
1798
        raise AssertionError()
1799
    self.rpc.call_instance_info.side_effect = _InstanceInfo
1800

    
1801
    self.rpc.call_bridges_exist.return_value = \
1802
      self.RpcResultsBuilder() \
1803
        .CreateSuccessfulNodeResult(self.master, True)
1804

    
1805
    self.rpc.call_blockdev_getmirrorstatus.side_effect = \
1806
      lambda node, _: self.RpcResultsBuilder() \
1807
                        .CreateSuccessfulNodeResult(node, [])
1808

    
1809
    self.rpc.call_blockdev_shutdown.side_effect = \
1810
      lambda node, _: self.RpcResultsBuilder() \
1811
                        .CreateSuccessfulNodeResult(node, [])
1812

    
1813
  def testNoChanges(self):
1814
    op = self.CopyOpCode(self.op)
1815
    self.ExecOpCodeExpectOpPrereqError(op, "No changes submitted")
1816

    
1817
  def testGlobalHvparams(self):
1818
    op = self.CopyOpCode(self.op,
1819
                         hvparams={constants.HV_MIGRATION_PORT: 1234})
1820
    self.ExecOpCodeExpectOpPrereqError(
1821
      op, "hypervisor parameters are global and cannot be customized")
1822

    
1823
  def testHvparams(self):
1824
    op = self.CopyOpCode(self.op,
1825
                         hvparams={constants.HV_BOOT_ORDER: "cd"})
1826
    self.ExecOpCode(op)
1827

    
1828
  def testDisksAndDiskTemplate(self):
1829
    op = self.CopyOpCode(self.op,
1830
                         disk_template=constants.DT_PLAIN,
1831
                         disks=[[constants.DDM_ADD, -1, {}]])
1832
    self.ExecOpCodeExpectOpPrereqError(
1833
      op, "Disk template conversion and other disk changes not supported at"
1834
          " the same time")
1835

    
1836
  def testDiskTemplateToMirroredNoRemoteNode(self):
1837
    op = self.CopyOpCode(self.op,
1838
                         disk_template=constants.DT_DRBD8)
1839
    self.ExecOpCodeExpectOpPrereqError(
1840
      op, "Changing the disk template to a mirrored one requires specifying"
1841
          " a secondary node")
1842

    
1843
  def testPrimaryNodeToOldPrimaryNode(self):
1844
    op = self.CopyOpCode(self.op,
1845
                         pnode=self.master.name)
1846
    self.ExecOpCode(op)
1847

    
1848
  def testPrimaryNodeChange(self):
1849
    node = self.cfg.AddNewNode()
1850
    op = self.CopyOpCode(self.op,
1851
                         pnode=node.name)
1852
    self.ExecOpCode(op)
1853

    
1854
  def testPrimaryNodeChangeRunningInstance(self):
1855
    node = self.cfg.AddNewNode()
1856
    op = self.CopyOpCode(self.running_op,
1857
                         pnode=node.name)
1858
    self.ExecOpCodeExpectOpPrereqError(op, "Instance is still running")
1859

    
1860
  def testOsChange(self):
1861
    os = self.cfg.CreateOs(supported_variants=[])
1862
    self.rpc.call_os_get.return_value = \
1863
      self.RpcResultsBuilder() \
1864
        .CreateSuccessfulNodeResult(self.master, os)
1865
    op = self.CopyOpCode(self.op,
1866
                         os_name=os.name)
1867
    self.ExecOpCode(op)
1868

    
1869
  def testVCpuChange(self):
1870
    op = self.CopyOpCode(self.op,
1871
                         beparams={
1872
                           constants.BE_VCPUS: 4
1873
                         })
1874
    self.ExecOpCode(op)
1875

    
1876
  def testWrongCpuMask(self):
1877
    op = self.CopyOpCode(self.op,
1878
                         beparams={
1879
                           constants.BE_VCPUS: 4
1880
                         },
1881
                         hvparams={
1882
                           constants.HV_CPU_MASK: "1,2:3,4"
1883
                         })
1884
    self.ExecOpCodeExpectOpPrereqError(
1885
      op, "Number of vCPUs .* does not match the CPU mask .*")
1886

    
1887
  def testCorrectCpuMask(self):
1888
    op = self.CopyOpCode(self.op,
1889
                         beparams={
1890
                           constants.BE_VCPUS: 4
1891
                         },
1892
                         hvparams={
1893
                           constants.HV_CPU_MASK: "1,2:3,4:all:1,4"
1894
                         })
1895
    self.ExecOpCode(op)
1896

    
1897
  def testOsParams(self):
1898
    op = self.CopyOpCode(self.op,
1899
                         osparams={
1900
                           self.os.supported_parameters[0]: "test_param_val"
1901
                         })
1902
    self.ExecOpCode(op)
1903

    
1904
  def testIncreaseMemoryTooMuch(self):
1905
    op = self.CopyOpCode(self.running_op,
1906
                         beparams={
1907
                           constants.BE_MAXMEM:
1908
                             self.mocked_master_memory_free * 2
1909
                         })
1910
    self.ExecOpCodeExpectOpPrereqError(
1911
      op, "This change will prevent the instance from starting")
1912

    
1913
  def testIncreaseMemory(self):
1914
    op = self.CopyOpCode(self.running_op,
1915
                         beparams={
1916
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1917
                         })
1918
    self.ExecOpCode(op)
1919

    
1920
  def testIncreaseMemoryTooMuchForSecondary(self):
1921
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP,
1922
                                   disk_template=constants.DT_DRBD8,
1923
                                   secondary_node=self.snode)
1924
    self.rpc.call_instance_info.side_effect = [
1925
      self.RpcResultsBuilder()
1926
        .CreateSuccessfulNodeResult(self.master,
1927
                                    {
1928
                                      "memory":
1929
                                        self.mocked_snode_memory_free * 2,
1930
                                      "vcpus": self.mocked_running_inst_vcpus,
1931
                                      "state": self.mocked_running_inst_state,
1932
                                      "time": self.mocked_running_inst_time
1933
                                    })]
1934

    
1935
    op = self.CopyOpCode(self.op,
1936
                         instance_name=inst.name,
1937
                         beparams={
1938
                           constants.BE_MAXMEM:
1939
                             self.mocked_snode_memory_free * 2,
1940
                           constants.BE_AUTO_BALANCE: True
1941
                         })
1942
    self.ExecOpCodeExpectOpPrereqError(
1943
      op, "This change will prevent the instance from failover to its"
1944
          " secondary node")
1945

    
1946
  def testInvalidRuntimeMemory(self):
1947
    op = self.CopyOpCode(self.running_op,
1948
                         runtime_mem=self.mocked_master_memory_free * 2)
1949
    self.ExecOpCodeExpectOpPrereqError(
1950
      op, "Instance .* must have memory between .* and .* of memory")
1951

    
1952
  def testIncreaseRuntimeMemory(self):
1953
    op = self.CopyOpCode(self.running_op,
1954
                         runtime_mem=self.mocked_master_memory_free,
1955
                         beparams={
1956
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1957
                         })
1958
    self.ExecOpCode(op)
1959

    
1960
  def testAddNicWithPoolIpNoNetwork(self):
1961
    op = self.CopyOpCode(self.op,
1962
                         nics=[(constants.DDM_ADD, -1,
1963
                                {
1964
                                  constants.INIC_IP: constants.NIC_IP_POOL
1965
                                })])
1966
    self.ExecOpCodeExpectOpPrereqError(
1967
      op, "If ip=pool, parameter network cannot be none")
1968

    
1969
  def testAddNicWithPoolIp(self):
1970
    net = self.cfg.AddNewNetwork()
1971
    self.cfg.ConnectNetworkToGroup(net, self.group)
1972
    op = self.CopyOpCode(self.op,
1973
                         nics=[(constants.DDM_ADD, -1,
1974
                                {
1975
                                  constants.INIC_IP: constants.NIC_IP_POOL,
1976
                                  constants.INIC_NETWORK: net.name
1977
                                })])
1978
    self.ExecOpCode(op)
1979

    
1980
  def testAddNicWithInvalidIp(self):
1981
    op = self.CopyOpCode(self.op,
1982
                         nics=[(constants.DDM_ADD, -1,
1983
                                {
1984
                                  constants.INIC_IP: "invalid"
1985
                                })])
1986
    self.ExecOpCodeExpectOpPrereqError(
1987
      op, "Invalid IP address")
1988

    
1989
  def testAddNic(self):
1990
    op = self.CopyOpCode(self.op,
1991
                         nics=[(constants.DDM_ADD, -1, {})])
1992
    self.ExecOpCode(op)
1993

    
1994
  def testHotAddNic(self):
1995
    op = self.CopyOpCode(self.op,
1996
                         nics=[(constants.DDM_ADD, -1, {})],
1997
                         hotplug=True)
1998
    self.ExecOpCode(op)
1999
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2000
    self.assertTrue(self.rpc.call_hotplug_device.called)
2001

    
2002
  def testAddNicWithIp(self):
2003
    op = self.CopyOpCode(self.op,
2004
                         nics=[(constants.DDM_ADD, -1,
2005
                                {
2006
                                  constants.INIC_IP: "2.3.1.4"
2007
                                })])
2008
    self.ExecOpCode(op)
2009

    
2010
  def testModifyNicRoutedWithoutIp(self):
2011
    op = self.CopyOpCode(self.op,
2012
                         nics=[(constants.DDM_MODIFY, 0,
2013
                                {
2014
                                  constants.INIC_MODE: constants.NIC_MODE_ROUTED
2015
                                })])
2016
    self.ExecOpCodeExpectOpPrereqError(
2017
      op, "Cannot set the NIC IP address to None on a routed NIC")
2018

    
2019
  def testModifyNicSetMac(self):
2020
    op = self.CopyOpCode(self.op,
2021
                         nics=[(constants.DDM_MODIFY, 0,
2022
                                {
2023
                                  constants.INIC_MAC: "0a:12:95:15:bf:75"
2024
                                })])
2025
    self.ExecOpCode(op)
2026

    
2027
  def testModifyNicWithPoolIpNoNetwork(self):
2028
    op = self.CopyOpCode(self.op,
2029
                         nics=[(constants.DDM_MODIFY, -1,
2030
                                {
2031
                                  constants.INIC_IP: constants.NIC_IP_POOL
2032
                                })])
2033
    self.ExecOpCodeExpectOpPrereqError(
2034
      op, "ip=pool, but no network found")
2035

    
2036
  def testModifyNicSetNet(self):
2037
    old_net = self.cfg.AddNewNetwork()
2038
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2039
    inst = self.cfg.AddNewInstance(nics=[
2040
      self.cfg.CreateNic(network=old_net,
2041
                         ip="198.51.100.2")])
2042

    
2043
    new_net = self.cfg.AddNewNetwork(mac_prefix="be")
2044
    self.cfg.ConnectNetworkToGroup(new_net, self.group)
2045
    op = self.CopyOpCode(self.op,
2046
                         instance_name=inst.name,
2047
                         nics=[(constants.DDM_MODIFY, 0,
2048
                                {
2049
                                  constants.INIC_NETWORK: new_net.name
2050
                                })])
2051
    self.ExecOpCode(op)
2052

    
2053
  def testModifyNicSetLinkWhileConnected(self):
2054
    old_net = self.cfg.AddNewNetwork()
2055
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2056
    inst = self.cfg.AddNewInstance(nics=[
2057
      self.cfg.CreateNic(network=old_net)])
2058

    
2059
    op = self.CopyOpCode(self.op,
2060
                         instance_name=inst.name,
2061
                         nics=[(constants.DDM_MODIFY, 0,
2062
                                {
2063
                                  constants.INIC_LINK: "mock_link"
2064
                                })])
2065
    self.ExecOpCodeExpectOpPrereqError(
2066
      op, "Not allowed to change link or mode of a NIC that is connected"
2067
          " to a network")
2068

    
2069
  def testModifyNicSetNetAndIp(self):
2070
    net = self.cfg.AddNewNetwork(mac_prefix="be", network="123.123.123.0/24")
2071
    self.cfg.ConnectNetworkToGroup(net, self.group)
2072
    op = self.CopyOpCode(self.op,
2073
                         nics=[(constants.DDM_MODIFY, 0,
2074
                                {
2075
                                  constants.INIC_NETWORK: net.name,
2076
                                  constants.INIC_IP: "123.123.123.1"
2077
                                })])
2078
    self.ExecOpCode(op)
2079

    
2080
  def testModifyNic(self):
2081
    op = self.CopyOpCode(self.op,
2082
                         nics=[(constants.DDM_MODIFY, 0, {})])
2083
    self.ExecOpCode(op)
2084

    
2085
  def testHotModifyNic(self):
2086
    op = self.CopyOpCode(self.op,
2087
                         nics=[(constants.DDM_MODIFY, 0, {})],
2088
                         hotplug=True)
2089
    self.ExecOpCode(op)
2090
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2091
    self.assertTrue(self.rpc.call_hotplug_device.called)
2092

    
2093
  def testRemoveLastNic(self):
2094
    op = self.CopyOpCode(self.op,
2095
                         nics=[(constants.DDM_REMOVE, 0, {})])
2096
    self.ExecOpCodeExpectOpPrereqError(
2097
      op, "violates policy")
2098

    
2099
  def testRemoveNic(self):
2100
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2101
                                         self.cfg.CreateNic()])
2102
    op = self.CopyOpCode(self.op,
2103
                         instance_name=inst.name,
2104
                         nics=[(constants.DDM_REMOVE, 0, {})])
2105
    self.ExecOpCode(op)
2106

    
2107
  def testHotRemoveNic(self):
2108
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2109
                                         self.cfg.CreateNic()])
2110
    op = self.CopyOpCode(self.op,
2111
                         instance_name=inst.name,
2112
                         nics=[(constants.DDM_REMOVE, 0, {})],
2113
                         hotplug=True)
2114
    self.ExecOpCode(op)
2115
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2116
    self.assertTrue(self.rpc.call_hotplug_device.called)
2117

    
2118
  def testSetOffline(self):
2119
    op = self.CopyOpCode(self.op,
2120
                         offline=True)
2121
    self.ExecOpCode(op)
2122

    
2123
  def testUnsetOffline(self):
2124
    op = self.CopyOpCode(self.op,
2125
                         offline=False)
2126
    self.ExecOpCode(op)
2127

    
2128
  def testAddDiskInvalidMode(self):
2129
    op = self.CopyOpCode(self.op,
2130
                         disks=[[constants.DDM_ADD, -1,
2131
                                 {
2132
                                   constants.IDISK_MODE: "invalid"
2133
                                 }]])
2134
    self.ExecOpCodeExpectOpPrereqError(
2135
      op, "Invalid disk access mode 'invalid'")
2136

    
2137
  def testAddDiskMissingSize(self):
2138
    op = self.CopyOpCode(self.op,
2139
                         disks=[[constants.DDM_ADD, -1, {}]])
2140
    self.ExecOpCodeExpectOpPrereqError(
2141
      op, "Required disk parameter 'size' missing")
2142

    
2143
  def testAddDiskInvalidSize(self):
2144
    op = self.CopyOpCode(self.op,
2145
                         disks=[[constants.DDM_ADD, -1,
2146
                                 {
2147
                                   constants.IDISK_SIZE: "invalid"
2148
                                 }]])
2149
    self.ExecOpCodeExpectException(
2150
      op, errors.TypeEnforcementError, "is not a valid size")
2151

    
2152
  def testAddDiskRunningInstanceNoWaitForSync(self):
2153
    op = self.CopyOpCode(self.running_op,
2154
                         disks=[[constants.DDM_ADD, -1,
2155
                                 {
2156
                                   constants.IDISK_SIZE: 1024
2157
                                 }]],
2158
                         wait_for_sync=False)
2159
    self.ExecOpCodeExpectOpPrereqError(
2160
      op, "Can't add a disk to an instance with activated disks"
2161
          " and --no-wait-for-sync given.")
2162

    
2163
  def testAddDiskDownInstance(self):
2164
    op = self.CopyOpCode(self.op,
2165
                         disks=[[constants.DDM_ADD, -1,
2166
                                 {
2167
                                   constants.IDISK_SIZE: 1024
2168
                                 }]])
2169
    self.ExecOpCode(op)
2170

    
2171
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2172

    
2173
  def testAddDiskRunningInstance(self):
2174
    op = self.CopyOpCode(self.running_op,
2175
                         disks=[[constants.DDM_ADD, -1,
2176
                                 {
2177
                                   constants.IDISK_SIZE: 1024
2178
                                 }]])
2179
    self.ExecOpCode(op)
2180

    
2181
    self.assertFalse(self.rpc.call_blockdev_shutdown.called)
2182

    
2183
  def testAddDiskNoneName(self):
2184
    op = self.CopyOpCode(self.op,
2185
                         disks=[[constants.DDM_ADD, -1,
2186
                                 {
2187
                                   constants.IDISK_SIZE: 1024,
2188
                                   constants.IDISK_NAME: constants.VALUE_NONE
2189
                                 }]])
2190
    self.ExecOpCode(op)
2191

    
2192
  def testHotAddDisk(self):
2193
    self.rpc.call_blockdev_assemble.return_value = \
2194
      self.RpcResultsBuilder() \
2195
        .CreateSuccessfulNodeResult(self.master, ("/dev/mocked_path",
2196
                                    "/var/run/ganeti/instance-disks/mocked_d"))
2197
    op = self.CopyOpCode(self.op,
2198
                         disks=[[constants.DDM_ADD, -1,
2199
                                 {
2200
                                   constants.IDISK_SIZE: 1024,
2201
                                 }]],
2202
                         hotplug=True)
2203
    self.ExecOpCode(op)
2204
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2205
    self.assertTrue(self.rpc.call_blockdev_create.called)
2206
    self.assertTrue(self.rpc.call_blockdev_assemble.called)
2207
    self.assertTrue(self.rpc.call_hotplug_device.called)
2208

    
2209
  def testHotRemoveDisk(self):
2210
    inst = self.cfg.AddNewInstance(disks=[self.cfg.CreateDisk(),
2211
                                          self.cfg.CreateDisk()])
2212
    op = self.CopyOpCode(self.op,
2213
                         instance_name=inst.name,
2214
                         disks=[[constants.DDM_REMOVE, -1,
2215
                                 {}]],
2216
                         hotplug=True)
2217
    self.ExecOpCode(op)
2218
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2219
    self.assertTrue(self.rpc.call_hotplug_device.called)
2220
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2221
    self.assertTrue(self.rpc.call_blockdev_remove.called)
2222

    
2223
  def testModifyDiskWithSize(self):
2224
    op = self.CopyOpCode(self.op,
2225
                         disks=[[constants.DDM_MODIFY, 0,
2226
                                 {
2227
                                   constants.IDISK_SIZE: 1024
2228
                                 }]])
2229
    self.ExecOpCodeExpectOpPrereqError(
2230
      op, "Disk size change not possible, use grow-disk")
2231

    
2232
  def testModifyDiskWithRandomParams(self):
2233
    op = self.CopyOpCode(self.op,
2234
                         disks=[[constants.DDM_MODIFY, 0,
2235
                                 {
2236
                                   constants.IDISK_METAVG: "new_meta_vg",
2237
                                   constants.IDISK_MODE: "invalid",
2238
                                   constants.IDISK_NAME: "new_name"
2239
                                 }]])
2240
    self.ExecOpCodeExpectOpPrereqError(
2241
      op, "Disk modification doesn't support additional arbitrary parameters")
2242

    
2243
  def testModifyDiskUnsetName(self):
2244
    op = self.CopyOpCode(self.op,
2245
                         disks=[[constants.DDM_MODIFY, 0,
2246
                                  {
2247
                                    constants.IDISK_NAME: constants.VALUE_NONE
2248
                                  }]])
2249
    self.ExecOpCode(op)
2250

    
2251
  def testSetOldDiskTemplate(self):
2252
    op = self.CopyOpCode(self.op,
2253
                         disk_template=self.inst.disk_template)
2254
    self.ExecOpCodeExpectOpPrereqError(
2255
      op, "Instance already has disk template")
2256

    
2257
  def testSetDisabledDiskTemplate(self):
2258
    self.cfg.SetEnabledDiskTemplates([self.inst.disk_template])
2259
    op = self.CopyOpCode(self.op,
2260
                         disk_template=constants.DT_EXT)
2261
    self.ExecOpCodeExpectOpPrereqError(
2262
      op, "Disk template .* is not enabled for this cluster")
2263

    
2264
  def testInvalidDiskTemplateConversion(self):
2265
    op = self.CopyOpCode(self.op,
2266
                         disk_template=constants.DT_EXT)
2267
    self.ExecOpCodeExpectOpPrereqError(
2268
      op, "Unsupported disk template conversion from .* to .*")
2269

    
2270
  def testConvertToDRBDWithSecondarySameAsPrimary(self):
2271
    op = self.CopyOpCode(self.op,
2272
                         disk_template=constants.DT_DRBD8,
2273
                         remote_node=self.master.name)
2274
    self.ExecOpCodeExpectOpPrereqError(
2275
      op, "Given new secondary node .* is the same as the primary node"
2276
          " of the instance")
2277

    
2278
  def testConvertPlainToDRBD(self):
2279
    self.rpc.call_blockdev_shutdown.return_value = \
2280
      self.RpcResultsBuilder() \
2281
        .CreateSuccessfulNodeResult(self.master, True)
2282
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2283
      self.RpcResultsBuilder() \
2284
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2285

    
2286
    op = self.CopyOpCode(self.op,
2287
                         disk_template=constants.DT_DRBD8,
2288
                         remote_node=self.snode.name)
2289
    self.ExecOpCode(op)
2290

    
2291
  def testConvertDRBDToPlain(self):
2292
    self.inst.disks = [self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
2293
                                           primary_node=self.master,
2294
                                           secondary_node=self.snode)]
2295
    self.inst.disk_template = constants.DT_DRBD8
2296
    self.rpc.call_blockdev_shutdown.return_value = \
2297
      self.RpcResultsBuilder() \
2298
        .CreateSuccessfulNodeResult(self.master, True)
2299
    self.rpc.call_blockdev_remove.return_value = \
2300
      self.RpcResultsBuilder() \
2301
        .CreateSuccessfulNodeResult(self.master)
2302
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2303
      self.RpcResultsBuilder() \
2304
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2305

    
2306
    op = self.CopyOpCode(self.op,
2307
                         disk_template=constants.DT_PLAIN)
2308
    self.ExecOpCode(op)
2309

    
2310

    
2311
class TestLUInstanceChangeGroup(CmdlibTestCase):
2312
  def setUp(self):
2313
    super(TestLUInstanceChangeGroup, self).setUp()
2314

    
2315
    self.group2 = self.cfg.AddNewNodeGroup()
2316
    self.node2 = self.cfg.AddNewNode(group=self.group2)
2317
    self.inst = self.cfg.AddNewInstance()
2318
    self.op = opcodes.OpInstanceChangeGroup(instance_name=self.inst.name)
2319

    
2320
  def testTargetGroupIsInstanceGroup(self):
2321
    op = self.CopyOpCode(self.op,
2322
                         target_groups=[self.group.name])
2323
    self.ExecOpCodeExpectOpPrereqError(
2324
      op, "Can't use group\(s\) .* as targets, they are used by the"
2325
          " instance .*")
2326

    
2327
  def testNoTargetGroups(self):
2328
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
2329
                                   primary_node=self.master,
2330
                                   secondary_node=self.node2)
2331
    op = self.CopyOpCode(self.op,
2332
                         instance_name=inst.name)
2333
    self.ExecOpCodeExpectOpPrereqError(
2334
      op, "There are no possible target groups")
2335

    
2336
  def testFailingIAllocator(self):
2337
    self.iallocator_cls.return_value.success = False
2338
    op = self.CopyOpCode(self.op)
2339

    
2340
    self.ExecOpCodeExpectOpPrereqError(
2341
      op, "Can't compute solution for changing group of instance .*"
2342
          " using iallocator .*")
2343

    
2344
  def testChangeGroup(self):
2345
    self.iallocator_cls.return_value.success = True
2346
    self.iallocator_cls.return_value.result = ([], [], [])
2347
    op = self.CopyOpCode(self.op)
2348

    
2349
    self.ExecOpCode(op)
2350

    
2351

    
2352
if __name__ == "__main__":
2353
  testutils.GanetiTestProgram()