Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / instance_unittest.py @ ac156ecd

History | View | Annotate | Download (83.5 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 import 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
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1110
        (constants.FD_BLKTAP, "/tmp/disk2"),
1111
        (constants.FD_BLKTAP, "/tmp/disk3"),
1112
        (constants.FD_BLKTAP, "/tmp/disk4"),
1113
        ])
1114

    
1115
  def testBlock(self):
1116
    disk_info = [{
1117
      constants.IDISK_SIZE: 8 * 1024,
1118
      constants.IDISK_MODE: constants.DISK_RDWR,
1119
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1120
      }]
1121

    
1122
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1123
                                   constants.DT_BLOCK)
1124

    
1125
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1126
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1127
      ])
1128

    
1129
  def testRbd(self):
1130
    disk_info = [{
1131
      constants.IDISK_SIZE: 8 * 1024,
1132
      constants.IDISK_MODE: constants.DISK_RDONLY,
1133
      }, {
1134
      constants.IDISK_SIZE: 100 * 1024,
1135
      constants.IDISK_MODE: constants.DISK_RDWR,
1136
      }]
1137

    
1138
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1139
                                   constants.DT_RBD)
1140

    
1141
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1142
      ("rbd", "ec1-uq0.rbd.disk0"),
1143
      ("rbd", "ec1-uq1.rbd.disk1"),
1144
      ])
1145

    
1146
  def testDrbd8(self):
1147
    gdt = instance.GenerateDiskTemplate
1148
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.DT_DRBD8]
1149
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1150

    
1151
    disk_info = [{
1152
      constants.IDISK_SIZE: 1024,
1153
      constants.IDISK_MODE: constants.DISK_RDWR,
1154
      }, {
1155
      constants.IDISK_SIZE: 100 * 1024,
1156
      constants.IDISK_MODE: constants.DISK_RDONLY,
1157
      constants.IDISK_METAVG: "metavg",
1158
      }, {
1159
      constants.IDISK_SIZE: 4096,
1160
      constants.IDISK_MODE: constants.DISK_RDWR,
1161
      constants.IDISK_VG: "vgxyz",
1162
      },
1163
      ]
1164

    
1165
    exp_logical_ids = [
1166
      [
1167
        (self.lu.cfg.GetVGName(), "ec1-uq0.disk0_data"),
1168
        (drbd8_default_metavg, "ec1-uq0.disk0_meta"),
1169
      ], [
1170
        (self.lu.cfg.GetVGName(), "ec1-uq1.disk1_data"),
1171
        ("metavg", "ec1-uq1.disk1_meta"),
1172
      ], [
1173
        ("vgxyz", "ec1-uq2.disk2_data"),
1174
        (drbd8_default_metavg, "ec1-uq2.disk2_meta"),
1175
      ]]
1176

    
1177
    assert len(exp_logical_ids) == len(disk_info)
1178

    
1179
    map(lambda params: utils.ForceDictType(params,
1180
                                           constants.IDISK_PARAMS_TYPES),
1181
        disk_info)
1182

    
1183
    # Check if empty list of secondaries is rejected
1184
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1185
                      "inst827.example.com", "node1334.example.com", [],
1186
                      disk_info, NotImplemented, NotImplemented, 0,
1187
                      self.lu.LogInfo, self.GetDiskParams())
1188

    
1189
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1190
                 "node1334.example.com", ["node12272.example.com"],
1191
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1192
                 self.GetDiskParams())
1193

    
1194
    for (idx, disk) in enumerate(result):
1195
      self.assertTrue(isinstance(disk, objects.Disk))
1196
      self.assertEqual(disk.dev_type, constants.DT_DRBD8)
1197
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1198
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1199

    
1200
      for child in disk.children:
1201
        self.assertTrue(isinstance(disk, objects.Disk))
1202
        self.assertEqual(child.dev_type, constants.DT_PLAIN)
1203
        self.assertTrue(child.children is None)
1204

    
1205
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1206
                       exp_logical_ids[idx])
1207

    
1208
      self.assertEqual(len(disk.children), 2)
1209
      self.assertEqual(disk.children[0].size, disk.size)
1210
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1211

    
1212
    self._CheckIvNames(result, 0, len(disk_info))
1213
    instance._UpdateIvNames(0, result)
1214
    self._CheckIvNames(result, 0, len(disk_info))
1215

    
1216
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1217
      ("node1334.example.com", "node12272.example.com",
1218
       constants.FIRST_DRBD_PORT, 20, 21, "ec1-secret0"),
1219
      ("node1334.example.com", "node12272.example.com",
1220
       constants.FIRST_DRBD_PORT + 1, 22, 23, "ec1-secret1"),
1221
      ("node1334.example.com", "node12272.example.com",
1222
       constants.FIRST_DRBD_PORT + 2, 24, 25, "ec1-secret2"),
1223
      ])
1224

    
1225

    
1226
class _DiskPauseTracker:
1227
  def __init__(self):
1228
    self.history = []
1229

    
1230
  def __call__(self, (disks, instance), pause):
1231
    assert not (set(disks) - set(instance.disks))
1232

    
1233
    self.history.extend((i.logical_id, i.size, pause)
1234
                        for i in disks)
1235

    
1236
    return (True, [True] * len(disks))
1237

    
1238

    
1239
class _ConfigForDiskWipe:
1240
  def __init__(self, exp_node_uuid):
1241
    self._exp_node_uuid = exp_node_uuid
1242

    
1243
  def GetNodeName(self, node_uuid):
1244
    assert node_uuid == self._exp_node_uuid
1245
    return "name.of.expected.node"
1246

    
1247

    
1248
class _RpcForDiskWipe:
1249
  def __init__(self, exp_node, pause_cb, wipe_cb):
1250
    self._exp_node = exp_node
1251
    self._pause_cb = pause_cb
1252
    self._wipe_cb = wipe_cb
1253

    
1254
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1255
    assert node == self._exp_node
1256
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1257

    
1258
  def call_blockdev_wipe(self, node, bdev, offset, size):
1259
    assert node == self._exp_node
1260
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1261

    
1262

    
1263
class _DiskWipeProgressTracker:
1264
  def __init__(self, start_offset):
1265
    self._start_offset = start_offset
1266
    self.progress = {}
1267

    
1268
  def __call__(self, (disk, _), offset, size):
1269
    assert isinstance(offset, (long, int))
1270
    assert isinstance(size, (long, int))
1271

    
1272
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1273

    
1274
    assert offset >= self._start_offset
1275
    assert (offset + size) <= disk.size
1276

    
1277
    assert size > 0
1278
    assert size <= constants.MAX_WIPE_CHUNK
1279
    assert size <= max_chunk_size
1280

    
1281
    assert offset == self._start_offset or disk.logical_id in self.progress
1282

    
1283
    # Keep track of progress
1284
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1285

    
1286
    assert cur_progress == offset
1287

    
1288
    # Record progress
1289
    self.progress[disk.logical_id] += size
1290

    
1291
    return (True, None)
1292

    
1293

    
1294
class TestWipeDisks(unittest.TestCase):
1295
  def _FailingPauseCb(self, (disks, _), pause):
1296
    self.assertEqual(len(disks), 3)
1297
    self.assertTrue(pause)
1298
    # Simulate an RPC error
1299
    return (False, "error")
1300

    
1301
  def testPauseFailure(self):
1302
    node_name = "node1372.example.com"
1303

    
1304
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1305
                                     NotImplemented),
1306
                 cfg=_ConfigForDiskWipe(node_name))
1307

    
1308
    disks = [
1309
      objects.Disk(dev_type=constants.DT_PLAIN),
1310
      objects.Disk(dev_type=constants.DT_PLAIN),
1311
      objects.Disk(dev_type=constants.DT_PLAIN),
1312
      ]
1313

    
1314
    inst = objects.Instance(name="inst21201",
1315
                            primary_node=node_name,
1316
                            disk_template=constants.DT_PLAIN,
1317
                            disks=disks)
1318

    
1319
    self.assertRaises(errors.OpExecError, instance.WipeDisks, lu, inst)
1320

    
1321
  def _FailingWipeCb(self, (disk, _), offset, size):
1322
    # This should only ever be called for the first disk
1323
    self.assertEqual(disk.logical_id, "disk0")
1324
    return (False, None)
1325

    
1326
  def testFailingWipe(self):
1327
    node_uuid = "node13445-uuid"
1328
    pt = _DiskPauseTracker()
1329

    
1330
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_uuid, pt, self._FailingWipeCb),
1331
                 cfg=_ConfigForDiskWipe(node_uuid))
1332

    
1333
    disks = [
1334
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0",
1335
                   size=100 * 1024),
1336
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1337
                   size=500 * 1024),
1338
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk2", size=256),
1339
      ]
1340

    
1341
    inst = objects.Instance(name="inst562",
1342
                            primary_node=node_uuid,
1343
                            disk_template=constants.DT_PLAIN,
1344
                            disks=disks)
1345

    
1346
    try:
1347
      instance.WipeDisks(lu, inst)
1348
    except errors.OpExecError, err:
1349
      self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1350
    else:
1351
      self.fail("Did not raise exception")
1352

    
1353
    # Check if all disks were paused and resumed
1354
    self.assertEqual(pt.history, [
1355
      ("disk0", 100 * 1024, True),
1356
      ("disk1", 500 * 1024, True),
1357
      ("disk2", 256, True),
1358
      ("disk0", 100 * 1024, False),
1359
      ("disk1", 500 * 1024, False),
1360
      ("disk2", 256, False),
1361
      ])
1362

    
1363
  def _PrepareWipeTest(self, start_offset, disks):
1364
    node_name = "node-with-offset%s.example.com" % start_offset
1365
    pauset = _DiskPauseTracker()
1366
    progresst = _DiskWipeProgressTracker(start_offset)
1367

    
1368
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1369
                 cfg=_ConfigForDiskWipe(node_name))
1370

    
1371
    instance = objects.Instance(name="inst3560",
1372
                                primary_node=node_name,
1373
                                disk_template=constants.DT_PLAIN,
1374
                                disks=disks)
1375

    
1376
    return (lu, instance, pauset, progresst)
1377

    
1378
  def testNormalWipe(self):
1379
    disks = [
1380
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0", size=1024),
1381
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1382
                   size=500 * 1024),
1383
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk2", size=128),
1384
      objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk3",
1385
                   size=constants.MAX_WIPE_CHUNK),
1386
      ]
1387

    
1388
    (lu, inst, pauset, progresst) = self._PrepareWipeTest(0, disks)
1389

    
1390
    instance.WipeDisks(lu, inst)
1391

    
1392
    self.assertEqual(pauset.history, [
1393
      ("disk0", 1024, True),
1394
      ("disk1", 500 * 1024, True),
1395
      ("disk2", 128, True),
1396
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1397
      ("disk0", 1024, False),
1398
      ("disk1", 500 * 1024, False),
1399
      ("disk2", 128, False),
1400
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1401
      ])
1402

    
1403
    # Ensure the complete disk has been wiped
1404
    self.assertEqual(progresst.progress,
1405
                     dict((i.logical_id, i.size) for i in disks))
1406

    
1407
  def testWipeWithStartOffset(self):
1408
    for start_offset in [0, 280, 8895, 1563204]:
1409
      disks = [
1410
        objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0",
1411
                     size=128),
1412
        objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1413
                     size=start_offset + (100 * 1024)),
1414
        ]
1415

    
1416
      (lu, inst, pauset, progresst) = \
1417
        self._PrepareWipeTest(start_offset, disks)
1418

    
1419
      # Test start offset with only one disk
1420
      instance.WipeDisks(lu, inst,
1421
                         disks=[(1, disks[1], start_offset)])
1422

    
1423
      # Only the second disk may have been paused and wiped
1424
      self.assertEqual(pauset.history, [
1425
        ("disk1", start_offset + (100 * 1024), True),
1426
        ("disk1", start_offset + (100 * 1024), False),
1427
        ])
1428
      self.assertEqual(progresst.progress, {
1429
        "disk1": disks[1].size,
1430
        })
1431

    
1432

    
1433
class TestCheckOpportunisticLocking(unittest.TestCase):
1434
  class OpTest(opcodes.OpCode):
1435
    OP_PARAMS = [
1436
      ("opportunistic_locking", False, ht.TBool, None),
1437
      ("iallocator", None, ht.TMaybe(ht.TNonEmptyString), "")
1438
      ]
1439

    
1440
  @classmethod
1441
  def _MakeOp(cls, **kwargs):
1442
    op = cls.OpTest(**kwargs)
1443
    op.Validate(True)
1444
    return op
1445

    
1446
  def testMissingAttributes(self):
1447
    self.assertRaises(AttributeError, instance._CheckOpportunisticLocking,
1448
                      object())
1449

    
1450
  def testDefaults(self):
1451
    op = self._MakeOp()
1452
    instance._CheckOpportunisticLocking(op)
1453

    
1454
  def test(self):
1455
    for iallocator in [None, "something", "other"]:
1456
      for opplock in [False, True]:
1457
        op = self._MakeOp(iallocator=iallocator,
1458
                          opportunistic_locking=opplock)
1459
        if opplock and not iallocator:
1460
          self.assertRaises(errors.OpPrereqError,
1461
                            instance._CheckOpportunisticLocking, op)
1462
        else:
1463
          instance._CheckOpportunisticLocking(op)
1464

    
1465

    
1466
class TestLUInstanceRemove(CmdlibTestCase):
1467
  def testRemoveMissingInstance(self):
1468
    op = opcodes.OpInstanceRemove(instance_name="missing.inst")
1469
    self.ExecOpCodeExpectOpPrereqError(op, "Instance 'missing.inst' not known")
1470

    
1471
  def testRemoveInst(self):
1472
    inst = self.cfg.AddNewInstance(disks=[])
1473
    op = opcodes.OpInstanceRemove(instance_name=inst.name)
1474
    self.ExecOpCode(op)
1475

    
1476

    
1477
class TestLUInstanceMove(CmdlibTestCase):
1478
  def setUp(self):
1479
    super(TestLUInstanceMove, self).setUp()
1480

    
1481
    self.node = self.cfg.AddNewNode()
1482

    
1483
    self.rpc.call_blockdev_assemble.return_value = \
1484
      self.RpcResultsBuilder() \
1485
        .CreateSuccessfulNodeResult(self.node, ("/dev/mocked_path",
1486
                                    "/var/run/ganeti/instance-disks/mocked_d"))
1487
    self.rpc.call_blockdev_remove.return_value = \
1488
      self.RpcResultsBuilder() \
1489
        .CreateSuccessfulNodeResult(self.master, "")
1490

    
1491
    def ImportStart(node_uuid, opt, inst, component, args):
1492
      return self.RpcResultsBuilder() \
1493
               .CreateSuccessfulNodeResult(node_uuid,
1494
                                           "deamon_on_%s" % node_uuid)
1495
    self.rpc.call_import_start.side_effect = ImportStart
1496

    
1497
    def ImpExpStatus(node_uuid, name):
1498
      return self.RpcResultsBuilder() \
1499
               .CreateSuccessfulNodeResult(node_uuid,
1500
                                           [objects.ImportExportStatus(
1501
                                             exit_status=0
1502
                                           )])
1503
    self.rpc.call_impexp_status.side_effect = ImpExpStatus
1504

    
1505
    def ImpExpCleanup(node_uuid, name):
1506
      return self.RpcResultsBuilder() \
1507
               .CreateSuccessfulNodeResult(node_uuid)
1508
    self.rpc.call_impexp_cleanup.side_effect = ImpExpCleanup
1509

    
1510
  def testMissingInstance(self):
1511
    op = opcodes.OpInstanceMove(instance_name="missing.inst",
1512
                                target_node=self.node.name)
1513
    self.ExecOpCodeExpectOpPrereqError(op, "Instance 'missing.inst' not known")
1514

    
1515
  def testUncopyableDiskTemplate(self):
1516
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_SHARED_FILE)
1517
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1518
                                target_node=self.node.name)
1519
    self.ExecOpCodeExpectOpPrereqError(
1520
      op, "Disk template sharedfile not suitable for copying")
1521

    
1522
  def testAlreadyOnTargetNode(self):
1523
    inst = self.cfg.AddNewInstance()
1524
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1525
                                target_node=self.master.name)
1526
    self.ExecOpCodeExpectOpPrereqError(
1527
      op, "Instance .* is already on the node .*")
1528

    
1529
  def testMoveStoppedInstance(self):
1530
    inst = self.cfg.AddNewInstance()
1531
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1532
                                target_node=self.node.name)
1533
    self.ExecOpCode(op)
1534

    
1535
  def testMoveRunningInstance(self):
1536
    self.rpc.call_node_info.return_value = \
1537
      self.RpcResultsBuilder() \
1538
        .AddSuccessfulNode(self.node,
1539
                           (NotImplemented, NotImplemented,
1540
                            ({"memory_free": 10000}, ))) \
1541
        .Build()
1542
    self.rpc.call_instance_start.return_value = \
1543
      self.RpcResultsBuilder() \
1544
        .CreateSuccessfulNodeResult(self.node, "")
1545

    
1546
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1547
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1548
                                target_node=self.node.name)
1549
    self.ExecOpCode(op)
1550

    
1551
  def testMoveFailingStartInstance(self):
1552
    self.rpc.call_node_info.return_value = \
1553
      self.RpcResultsBuilder() \
1554
        .AddSuccessfulNode(self.node,
1555
                           (NotImplemented, NotImplemented,
1556
                            ({"memory_free": 10000}, ))) \
1557
        .Build()
1558
    self.rpc.call_instance_start.return_value = \
1559
      self.RpcResultsBuilder() \
1560
        .CreateFailedNodeResult(self.node)
1561

    
1562
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1563
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1564
                                target_node=self.node.name)
1565
    self.ExecOpCodeExpectOpExecError(
1566
      op, "Could not start instance .* on node .*")
1567

    
1568
  def testMoveFailingImpExpDaemonExitCode(self):
1569
    inst = self.cfg.AddNewInstance()
1570
    self.rpc.call_impexp_status.side_effect = None
1571
    self.rpc.call_impexp_status.return_value = \
1572
      self.RpcResultsBuilder() \
1573
        .CreateSuccessfulNodeResult(self.node,
1574
                                    [objects.ImportExportStatus(
1575
                                      exit_status=1,
1576
                                      recent_output=["mock output"]
1577
                                    )])
1578
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1579
                                target_node=self.node.name)
1580
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1581

    
1582
  def testMoveFailingStartImpExpDaemon(self):
1583
    inst = self.cfg.AddNewInstance()
1584
    self.rpc.call_import_start.side_effect = None
1585
    self.rpc.call_import_start.return_value = \
1586
      self.RpcResultsBuilder() \
1587
        .CreateFailedNodeResult(self.node)
1588
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1589
                                target_node=self.node.name)
1590
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1591

    
1592

    
1593
class TestLUInstanceRename(CmdlibTestCase):
1594
  def setUp(self):
1595
    super(TestLUInstanceRename, self).setUp()
1596

    
1597
    self.inst = self.cfg.AddNewInstance()
1598

    
1599
    self.op = opcodes.OpInstanceRename(instance_name=self.inst.name,
1600
                                       new_name="new_name.example.com")
1601

    
1602
  def testIpCheckWithoutNameCheck(self):
1603
    op = self.CopyOpCode(self.op,
1604
                         ip_check=True,
1605
                         name_check=False)
1606
    self.ExecOpCodeExpectOpPrereqError(
1607
      op, "IP address check requires a name check")
1608

    
1609
  def testIpAlreadyInUse(self):
1610
    self.netutils_mod.TcpPing.return_value = True
1611
    op = self.CopyOpCode(self.op)
1612
    self.ExecOpCodeExpectOpPrereqError(
1613
      op, "IP .* of instance .* already in use")
1614

    
1615
  def testExistingInstanceName(self):
1616
    self.cfg.AddNewInstance(name="new_name.example.com")
1617
    op = self.CopyOpCode(self.op)
1618
    self.ExecOpCodeExpectOpPrereqError(
1619
      op, "Instance .* is already in the cluster")
1620

    
1621
  def testFileInstance(self):
1622
    self.rpc.call_blockdev_assemble.return_value = \
1623
      self.RpcResultsBuilder() \
1624
        .CreateSuccessfulNodeResult(self.master, (None, None))
1625
    self.rpc.call_blockdev_shutdown.return_value = \
1626
      self.RpcResultsBuilder() \
1627
        .CreateSuccessfulNodeResult(self.master, (None, None))
1628

    
1629
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_FILE)
1630
    op = self.CopyOpCode(self.op,
1631
                         instance_name=inst.name)
1632
    self.ExecOpCode(op)
1633

    
1634

    
1635
class TestLUInstanceMultiAlloc(CmdlibTestCase):
1636
  def setUp(self):
1637
    super(TestLUInstanceMultiAlloc, self).setUp()
1638

    
1639
    self.inst_op = opcodes.OpInstanceCreate(instance_name="inst.example.com",
1640
                                            disk_template=constants.DT_DRBD8,
1641
                                            disks=[],
1642
                                            nics=[],
1643
                                            os_type="mock_os",
1644
                                            hypervisor=constants.HT_XEN_HVM,
1645
                                            mode=constants.INSTANCE_CREATE)
1646

    
1647
  def testInstanceWithIAllocator(self):
1648
    inst = self.CopyOpCode(self.inst_op,
1649
                           iallocator="mock")
1650
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1651
    self.ExecOpCodeExpectOpPrereqError(
1652
      op, "iallocator are not allowed to be set on instance objects")
1653

    
1654
  def testOnlySomeNodesGiven(self):
1655
    inst1 = self.CopyOpCode(self.inst_op,
1656
                            pnode=self.master.name)
1657
    inst2 = self.CopyOpCode(self.inst_op)
1658
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1659
    self.ExecOpCodeExpectOpPrereqError(
1660
      op, "There are instance objects providing pnode/snode while others"
1661
          " do not")
1662

    
1663
  def testMissingIAllocator(self):
1664
    self.cluster.default_iallocator = None
1665
    inst = self.CopyOpCode(self.inst_op)
1666
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1667
    self.ExecOpCodeExpectOpPrereqError(
1668
      op, "No iallocator or nodes on the instances given and no cluster-wide"
1669
          " default iallocator found")
1670

    
1671
  def testDuplicateInstanceNames(self):
1672
    inst1 = self.CopyOpCode(self.inst_op)
1673
    inst2 = self.CopyOpCode(self.inst_op)
1674
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1675
    self.ExecOpCodeExpectOpPrereqError(
1676
      op, "There are duplicate instance names")
1677

    
1678
  def testWithGivenNodes(self):
1679
    snode = self.cfg.AddNewNode()
1680
    inst = self.CopyOpCode(self.inst_op,
1681
                           pnode=self.master.name,
1682
                           snode=snode.name)
1683
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1684
    self.ExecOpCode(op)
1685

    
1686
  def testDryRun(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
                                      dry_run=True)
1693
    self.ExecOpCode(op)
1694

    
1695
  def testWithIAllocator(self):
1696
    snode = self.cfg.AddNewNode()
1697
    self.iallocator_cls.return_value.result = \
1698
      ([("inst.example.com", [self.master.name, snode.name])], [])
1699

    
1700
    inst = self.CopyOpCode(self.inst_op)
1701
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1702
                                      iallocator="mock_ialloc")
1703
    self.ExecOpCode(op)
1704

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

    
1710
    inst = self.CopyOpCode(self.inst_op)
1711
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1712
                                      iallocator="mock_ialloc",
1713
                                      opportunistic_locking=True)
1714
    self.ExecOpCode(op)
1715

    
1716
  def testFailingIAllocator(self):
1717
    self.iallocator_cls.return_value.success = False
1718

    
1719
    inst = self.CopyOpCode(self.inst_op)
1720
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1721
                                      iallocator="mock_ialloc")
1722
    self.ExecOpCodeExpectOpPrereqError(
1723
      op, "Can't compute nodes using iallocator")
1724

    
1725

    
1726
class TestLUInstanceSetParams(CmdlibTestCase):
1727
  def setUp(self):
1728
    super(TestLUInstanceSetParams, self).setUp()
1729

    
1730
    self.inst = self.cfg.AddNewInstance()
1731
    self.op = opcodes.OpInstanceSetParams(instance_name=self.inst.name)
1732

    
1733
    self.running_inst = \
1734
      self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1735
    self.running_op = \
1736
      opcodes.OpInstanceSetParams(instance_name=self.running_inst.name)
1737

    
1738
    self.snode = self.cfg.AddNewNode()
1739

    
1740
    self.mocked_storage_type = constants.ST_LVM_VG
1741
    self.mocked_storage_free = 10000
1742
    self.mocked_master_cpu_total = 16
1743
    self.mocked_master_memory_free = 2048
1744
    self.mocked_snode_cpu_total = 16
1745
    self.mocked_snode_memory_free = 512
1746

    
1747
    self.mocked_running_inst_memory = 1024
1748
    self.mocked_running_inst_vcpus = 8
1749
    self.mocked_running_inst_state = "running"
1750
    self.mocked_running_inst_time = 10938474
1751

    
1752
    bootid = "mock_bootid"
1753
    storage_info = [
1754
      {
1755
        "type": self.mocked_storage_type,
1756
        "storage_free": self.mocked_storage_free
1757
      }
1758
    ]
1759
    hv_info_master = {
1760
      "cpu_total": self.mocked_master_cpu_total,
1761
      "memory_free": self.mocked_master_memory_free
1762
    }
1763
    hv_info_snode = {
1764
      "cpu_total": self.mocked_snode_cpu_total,
1765
      "memory_free": self.mocked_snode_memory_free
1766
    }
1767

    
1768
    self.rpc.call_node_info.return_value = \
1769
      self.RpcResultsBuilder() \
1770
        .AddSuccessfulNode(self.master,
1771
                           (bootid, storage_info, (hv_info_master, ))) \
1772
        .AddSuccessfulNode(self.snode,
1773
                           (bootid, storage_info, (hv_info_snode, ))) \
1774
        .Build()
1775

    
1776
    def _InstanceInfo(_, instance, __, ___):
1777
      if instance == self.inst.name:
1778
        return self.RpcResultsBuilder() \
1779
          .CreateSuccessfulNodeResult(self.master, None)
1780
      elif instance == self.running_inst.name:
1781
        return self.RpcResultsBuilder() \
1782
          .CreateSuccessfulNodeResult(
1783
            self.master, {
1784
              "memory": self.mocked_running_inst_memory,
1785
              "vcpus": self.mocked_running_inst_vcpus,
1786
              "state": self.mocked_running_inst_state,
1787
              "time": self.mocked_running_inst_time
1788
            })
1789
      else:
1790
        raise AssertionError()
1791
    self.rpc.call_instance_info.side_effect = _InstanceInfo
1792

    
1793
    self.rpc.call_bridges_exist.return_value = \
1794
      self.RpcResultsBuilder() \
1795
        .CreateSuccessfulNodeResult(self.master, True)
1796

    
1797
    self.rpc.call_blockdev_getmirrorstatus.side_effect = \
1798
      lambda node, _: self.RpcResultsBuilder() \
1799
                        .CreateSuccessfulNodeResult(node, [])
1800

    
1801
    self.rpc.call_blockdev_shutdown.side_effect = \
1802
      lambda node, _: self.RpcResultsBuilder() \
1803
                        .CreateSuccessfulNodeResult(node, [])
1804

    
1805
  def testNoChanges(self):
1806
    op = self.CopyOpCode(self.op)
1807
    self.ExecOpCodeExpectOpPrereqError(op, "No changes submitted")
1808

    
1809
  def testGlobalHvparams(self):
1810
    op = self.CopyOpCode(self.op,
1811
                         hvparams={constants.HV_MIGRATION_PORT: 1234})
1812
    self.ExecOpCodeExpectOpPrereqError(
1813
      op, "hypervisor parameters are global and cannot be customized")
1814

    
1815
  def testHvparams(self):
1816
    op = self.CopyOpCode(self.op,
1817
                         hvparams={constants.HV_BOOT_ORDER: "cd"})
1818
    self.ExecOpCode(op)
1819

    
1820
  def testDisksAndDiskTemplate(self):
1821
    op = self.CopyOpCode(self.op,
1822
                         disk_template=constants.DT_PLAIN,
1823
                         disks=[[constants.DDM_ADD, -1, {}]])
1824
    self.ExecOpCodeExpectOpPrereqError(
1825
      op, "Disk template conversion and other disk changes not supported at"
1826
          " the same time")
1827

    
1828
  def testDiskTemplateToMirroredNoRemoteNode(self):
1829
    op = self.CopyOpCode(self.op,
1830
                         disk_template=constants.DT_DRBD8)
1831
    self.ExecOpCodeExpectOpPrereqError(
1832
      op, "Changing the disk template to a mirrored one requires specifying"
1833
          " a secondary node")
1834

    
1835
  def testPrimaryNodeToOldPrimaryNode(self):
1836
    op = self.CopyOpCode(self.op,
1837
                         pnode=self.master.name)
1838
    self.ExecOpCode(op)
1839

    
1840
  def testPrimaryNodeChange(self):
1841
    node = self.cfg.AddNewNode()
1842
    op = self.CopyOpCode(self.op,
1843
                         pnode=node.name)
1844
    self.ExecOpCode(op)
1845

    
1846
  def testPrimaryNodeChangeRunningInstance(self):
1847
    node = self.cfg.AddNewNode()
1848
    op = self.CopyOpCode(self.running_op,
1849
                         pnode=node.name)
1850
    self.ExecOpCodeExpectOpPrereqError(op, "Instance is still running")
1851

    
1852
  def testOsChange(self):
1853
    os = self.cfg.CreateOs(supported_variants=[])
1854
    self.rpc.call_os_get.return_value = \
1855
      self.RpcResultsBuilder() \
1856
        .CreateSuccessfulNodeResult(self.master, os)
1857
    op = self.CopyOpCode(self.op,
1858
                         os_name=os.name)
1859
    self.ExecOpCode(op)
1860

    
1861
  def testVCpuChange(self):
1862
    op = self.CopyOpCode(self.op,
1863
                         beparams={
1864
                           constants.BE_VCPUS: 4
1865
                         })
1866
    self.ExecOpCode(op)
1867

    
1868
  def testWrongCpuMask(self):
1869
    op = self.CopyOpCode(self.op,
1870
                         beparams={
1871
                           constants.BE_VCPUS: 4
1872
                         },
1873
                         hvparams={
1874
                           constants.HV_CPU_MASK: "1,2:3,4"
1875
                         })
1876
    self.ExecOpCodeExpectOpPrereqError(
1877
      op, "Number of vCPUs .* does not match the CPU mask .*")
1878

    
1879
  def testCorrectCpuMask(self):
1880
    op = self.CopyOpCode(self.op,
1881
                         beparams={
1882
                           constants.BE_VCPUS: 4
1883
                         },
1884
                         hvparams={
1885
                           constants.HV_CPU_MASK: "1,2:3,4:all:1,4"
1886
                         })
1887
    self.ExecOpCode(op)
1888

    
1889
  def testOsParams(self):
1890
    op = self.CopyOpCode(self.op,
1891
                         osparams={
1892
                           self.os.supported_parameters[0]: "test_param_val"
1893
                         })
1894
    self.ExecOpCode(op)
1895

    
1896
  def testIncreaseMemoryTooMuch(self):
1897
    op = self.CopyOpCode(self.running_op,
1898
                         beparams={
1899
                           constants.BE_MAXMEM:
1900
                             self.mocked_master_memory_free * 2
1901
                         })
1902
    self.ExecOpCodeExpectOpPrereqError(
1903
      op, "This change will prevent the instance from starting")
1904

    
1905
  def testIncreaseMemory(self):
1906
    op = self.CopyOpCode(self.running_op,
1907
                         beparams={
1908
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1909
                         })
1910
    self.ExecOpCode(op)
1911

    
1912
  def testIncreaseMemoryTooMuchForSecondary(self):
1913
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP,
1914
                                   disk_template=constants.DT_DRBD8,
1915
                                   secondary_node=self.snode)
1916
    self.rpc.call_instance_info.side_effect = [
1917
      self.RpcResultsBuilder()
1918
        .CreateSuccessfulNodeResult(self.master,
1919
                                    {
1920
                                      "memory":
1921
                                        self.mocked_snode_memory_free * 2,
1922
                                      "vcpus": self.mocked_running_inst_vcpus,
1923
                                      "state": self.mocked_running_inst_state,
1924
                                      "time": self.mocked_running_inst_time
1925
                                    })]
1926

    
1927
    op = self.CopyOpCode(self.op,
1928
                         instance_name=inst.name,
1929
                         beparams={
1930
                           constants.BE_MAXMEM:
1931
                             self.mocked_snode_memory_free * 2,
1932
                           constants.BE_AUTO_BALANCE: True
1933
                         })
1934
    self.ExecOpCodeExpectOpPrereqError(
1935
      op, "This change will prevent the instance from failover to its"
1936
          " secondary node")
1937

    
1938
  def testInvalidRuntimeMemory(self):
1939
    op = self.CopyOpCode(self.running_op,
1940
                         runtime_mem=self.mocked_master_memory_free * 2)
1941
    self.ExecOpCodeExpectOpPrereqError(
1942
      op, "Instance .* must have memory between .* and .* of memory")
1943

    
1944
  def testIncreaseRuntimeMemory(self):
1945
    op = self.CopyOpCode(self.running_op,
1946
                         runtime_mem=self.mocked_master_memory_free,
1947
                         beparams={
1948
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1949
                         })
1950
    self.ExecOpCode(op)
1951

    
1952
  def testAddNicWithPoolIpNoNetwork(self):
1953
    op = self.CopyOpCode(self.op,
1954
                         nics=[(constants.DDM_ADD, -1,
1955
                                {
1956
                                  constants.INIC_IP: constants.NIC_IP_POOL
1957
                                })])
1958
    self.ExecOpCodeExpectOpPrereqError(
1959
      op, "If ip=pool, parameter network cannot be none")
1960

    
1961
  def testAddNicWithPoolIp(self):
1962
    net = self.cfg.AddNewNetwork()
1963
    self.cfg.ConnectNetworkToGroup(net, self.group)
1964
    op = self.CopyOpCode(self.op,
1965
                         nics=[(constants.DDM_ADD, -1,
1966
                                {
1967
                                  constants.INIC_IP: constants.NIC_IP_POOL,
1968
                                  constants.INIC_NETWORK: net.name
1969
                                })])
1970
    self.ExecOpCode(op)
1971

    
1972
  def testAddNicWithInvalidIp(self):
1973
    op = self.CopyOpCode(self.op,
1974
                         nics=[(constants.DDM_ADD, -1,
1975
                                {
1976
                                  constants.INIC_IP: "invalid"
1977
                                })])
1978
    self.ExecOpCodeExpectOpPrereqError(
1979
      op, "Invalid IP address")
1980

    
1981
  def testAddNic(self):
1982
    op = self.CopyOpCode(self.op,
1983
                         nics=[(constants.DDM_ADD, -1, {})])
1984
    self.ExecOpCode(op)
1985

    
1986
  def testHotAddNic(self):
1987
    op = self.CopyOpCode(self.op,
1988
                         nics=[(constants.DDM_ADD, -1, {})],
1989
                         hotplug=True)
1990
    self.ExecOpCode(op)
1991
    self.assertTrue(self.rpc.call_hotplug_supported.called)
1992
    self.assertTrue(self.rpc.call_hotplug_device.called)
1993

    
1994
  def testAddNicWithIp(self):
1995
    op = self.CopyOpCode(self.op,
1996
                         nics=[(constants.DDM_ADD, -1,
1997
                                {
1998
                                  constants.INIC_IP: "2.3.1.4"
1999
                                })])
2000
    self.ExecOpCode(op)
2001

    
2002
  def testModifyNicRoutedWithoutIp(self):
2003
    op = self.CopyOpCode(self.op,
2004
                         nics=[(constants.DDM_MODIFY, 0,
2005
                                {
2006
                                  constants.INIC_MODE: constants.NIC_MODE_ROUTED
2007
                                })])
2008
    self.ExecOpCodeExpectOpPrereqError(
2009
      op, "Cannot set the NIC IP address to None on a routed NIC")
2010

    
2011
  def testModifyNicSetMac(self):
2012
    op = self.CopyOpCode(self.op,
2013
                         nics=[(constants.DDM_MODIFY, 0,
2014
                                {
2015
                                  constants.INIC_MAC: "0a:12:95:15:bf:75"
2016
                                })])
2017
    self.ExecOpCode(op)
2018

    
2019
  def testModifyNicWithPoolIpNoNetwork(self):
2020
    op = self.CopyOpCode(self.op,
2021
                         nics=[(constants.DDM_MODIFY, -1,
2022
                                {
2023
                                  constants.INIC_IP: constants.NIC_IP_POOL
2024
                                })])
2025
    self.ExecOpCodeExpectOpPrereqError(
2026
      op, "ip=pool, but no network found")
2027

    
2028
  def testModifyNicSetNet(self):
2029
    old_net = self.cfg.AddNewNetwork()
2030
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2031
    inst = self.cfg.AddNewInstance(nics=[
2032
      self.cfg.CreateNic(network=old_net,
2033
                         ip="198.51.100.2")])
2034

    
2035
    new_net = self.cfg.AddNewNetwork(mac_prefix="be")
2036
    self.cfg.ConnectNetworkToGroup(new_net, self.group)
2037
    op = self.CopyOpCode(self.op,
2038
                         instance_name=inst.name,
2039
                         nics=[(constants.DDM_MODIFY, 0,
2040
                                {
2041
                                  constants.INIC_NETWORK: new_net.name
2042
                                })])
2043
    self.ExecOpCode(op)
2044

    
2045
  def testModifyNicSetLinkWhileConnected(self):
2046
    old_net = self.cfg.AddNewNetwork()
2047
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2048
    inst = self.cfg.AddNewInstance(nics=[
2049
      self.cfg.CreateNic(network=old_net)])
2050

    
2051
    op = self.CopyOpCode(self.op,
2052
                         instance_name=inst.name,
2053
                         nics=[(constants.DDM_MODIFY, 0,
2054
                                {
2055
                                  constants.INIC_LINK: "mock_link"
2056
                                })])
2057
    self.ExecOpCodeExpectOpPrereqError(
2058
      op, "Not allowed to change link or mode of a NIC that is connected"
2059
          " to a network")
2060

    
2061
  def testModifyNicSetNetAndIp(self):
2062
    net = self.cfg.AddNewNetwork(mac_prefix="be", network="123.123.123.0/24")
2063
    self.cfg.ConnectNetworkToGroup(net, self.group)
2064
    op = self.CopyOpCode(self.op,
2065
                         nics=[(constants.DDM_MODIFY, 0,
2066
                                {
2067
                                  constants.INIC_NETWORK: net.name,
2068
                                  constants.INIC_IP: "123.123.123.1"
2069
                                })])
2070
    self.ExecOpCode(op)
2071

    
2072
  def testModifyNic(self):
2073
    op = self.CopyOpCode(self.op,
2074
                         nics=[(constants.DDM_MODIFY, 0, {})])
2075
    self.ExecOpCode(op)
2076

    
2077
  def testHotModifyNic(self):
2078
    op = self.CopyOpCode(self.op,
2079
                         nics=[(constants.DDM_MODIFY, 0, {})],
2080
                         hotplug=True)
2081
    self.ExecOpCode(op)
2082
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2083
    self.assertTrue(self.rpc.call_hotplug_device.called)
2084

    
2085
  def testRemoveLastNic(self):
2086
    op = self.CopyOpCode(self.op,
2087
                         nics=[(constants.DDM_REMOVE, 0, {})])
2088
    self.ExecOpCodeExpectOpPrereqError(
2089
      op, "violates policy")
2090

    
2091
  def testRemoveNic(self):
2092
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2093
                                         self.cfg.CreateNic()])
2094
    op = self.CopyOpCode(self.op,
2095
                         instance_name=inst.name,
2096
                         nics=[(constants.DDM_REMOVE, 0, {})])
2097
    self.ExecOpCode(op)
2098

    
2099
  def testHotRemoveNic(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
                         hotplug=True)
2106
    self.ExecOpCode(op)
2107
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2108
    self.assertTrue(self.rpc.call_hotplug_device.called)
2109

    
2110
  def testSetOffline(self):
2111
    op = self.CopyOpCode(self.op,
2112
                         offline=True)
2113
    self.ExecOpCode(op)
2114

    
2115
  def testUnsetOffline(self):
2116
    op = self.CopyOpCode(self.op,
2117
                         offline=False)
2118
    self.ExecOpCode(op)
2119

    
2120
  def testAddDiskInvalidMode(self):
2121
    op = self.CopyOpCode(self.op,
2122
                         disks=[[constants.DDM_ADD, -1,
2123
                                 {
2124
                                   constants.IDISK_MODE: "invalid"
2125
                                 }]])
2126
    self.ExecOpCodeExpectOpPrereqError(
2127
      op, "Invalid disk access mode 'invalid'")
2128

    
2129
  def testAddDiskMissingSize(self):
2130
    op = self.CopyOpCode(self.op,
2131
                         disks=[[constants.DDM_ADD, -1, {}]])
2132
    self.ExecOpCodeExpectOpPrereqError(
2133
      op, "Required disk parameter 'size' missing")
2134

    
2135
  def testAddDiskInvalidSize(self):
2136
    op = self.CopyOpCode(self.op,
2137
                         disks=[[constants.DDM_ADD, -1,
2138
                                 {
2139
                                   constants.IDISK_SIZE: "invalid"
2140
                                 }]])
2141
    self.ExecOpCodeExpectException(
2142
      op, errors.TypeEnforcementError, "is not a valid size")
2143

    
2144
  def testAddDiskRunningInstanceNoWaitForSync(self):
2145
    op = self.CopyOpCode(self.running_op,
2146
                         disks=[[constants.DDM_ADD, -1,
2147
                                 {
2148
                                   constants.IDISK_SIZE: 1024
2149
                                 }]],
2150
                         wait_for_sync=False)
2151
    self.ExecOpCodeExpectOpPrereqError(
2152
      op, "Can't add a disk to an instance with activated disks"
2153
          " and --no-wait-for-sync given.")
2154

    
2155
  def testAddDiskDownInstance(self):
2156
    op = self.CopyOpCode(self.op,
2157
                         disks=[[constants.DDM_ADD, -1,
2158
                                 {
2159
                                   constants.IDISK_SIZE: 1024
2160
                                 }]])
2161
    self.ExecOpCode(op)
2162

    
2163
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2164

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

    
2173
    self.assertFalse(self.rpc.call_blockdev_shutdown.called)
2174

    
2175
  def testAddDiskNoneName(self):
2176
    op = self.CopyOpCode(self.op,
2177
                         disks=[[constants.DDM_ADD, -1,
2178
                                 {
2179
                                   constants.IDISK_SIZE: 1024,
2180
                                   constants.IDISK_NAME: constants.VALUE_NONE
2181
                                 }]])
2182
    self.ExecOpCode(op)
2183

    
2184
  def testHotAddDisk(self):
2185
    self.rpc.call_blockdev_assemble.return_value = \
2186
      self.RpcResultsBuilder() \
2187
        .CreateSuccessfulNodeResult(self.master, ("/dev/mocked_path",
2188
                                    "/var/run/ganeti/instance-disks/mocked_d"))
2189
    op = self.CopyOpCode(self.op,
2190
                         disks=[[constants.DDM_ADD, -1,
2191
                                 {
2192
                                   constants.IDISK_SIZE: 1024,
2193
                                 }]],
2194
                         hotplug=True)
2195
    self.ExecOpCode(op)
2196
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2197
    self.assertTrue(self.rpc.call_blockdev_create.called)
2198
    self.assertTrue(self.rpc.call_blockdev_assemble.called)
2199
    self.assertTrue(self.rpc.call_hotplug_device.called)
2200

    
2201
  def testHotRemoveDisk(self):
2202
    inst = self.cfg.AddNewInstance(disks=[self.cfg.CreateDisk(),
2203
                                          self.cfg.CreateDisk()])
2204
    op = self.CopyOpCode(self.op,
2205
                         instance_name=inst.name,
2206
                         disks=[[constants.DDM_REMOVE, -1,
2207
                                 {}]],
2208
                         hotplug=True)
2209
    self.ExecOpCode(op)
2210
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2211
    self.assertTrue(self.rpc.call_hotplug_device.called)
2212
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2213
    self.assertTrue(self.rpc.call_blockdev_remove.called)
2214

    
2215
  def testModifyDiskWithSize(self):
2216
    op = self.CopyOpCode(self.op,
2217
                         disks=[[constants.DDM_MODIFY, 0,
2218
                                 {
2219
                                   constants.IDISK_SIZE: 1024
2220
                                 }]])
2221
    self.ExecOpCodeExpectOpPrereqError(
2222
      op, "Disk size change not possible, use grow-disk")
2223

    
2224
  def testModifyDiskWithRandomParams(self):
2225
    op = self.CopyOpCode(self.op,
2226
                         disks=[[constants.DDM_MODIFY, 0,
2227
                                 {
2228
                                   constants.IDISK_METAVG: "new_meta_vg",
2229
                                   constants.IDISK_MODE: "invalid",
2230
                                   constants.IDISK_NAME: "new_name"
2231
                                 }]])
2232
    self.ExecOpCodeExpectOpPrereqError(
2233
      op, "Disk modification doesn't support additional arbitrary parameters")
2234

    
2235
  def testModifyDiskUnsetName(self):
2236
    op = self.CopyOpCode(self.op,
2237
                         disks=[[constants.DDM_MODIFY, 0,
2238
                                  {
2239
                                    constants.IDISK_NAME: constants.VALUE_NONE
2240
                                  }]])
2241
    self.ExecOpCode(op)
2242

    
2243
  def testSetOldDiskTemplate(self):
2244
    op = self.CopyOpCode(self.op,
2245
                         disk_template=self.inst.disk_template)
2246
    self.ExecOpCodeExpectOpPrereqError(
2247
      op, "Instance already has disk template")
2248

    
2249
  def testSetDisabledDiskTemplate(self):
2250
    self.cfg.SetEnabledDiskTemplates([self.inst.disk_template])
2251
    op = self.CopyOpCode(self.op,
2252
                         disk_template=constants.DT_EXT)
2253
    self.ExecOpCodeExpectOpPrereqError(
2254
      op, "Disk template .* is not enabled for this cluster")
2255

    
2256
  def testInvalidDiskTemplateConversion(self):
2257
    op = self.CopyOpCode(self.op,
2258
                         disk_template=constants.DT_EXT)
2259
    self.ExecOpCodeExpectOpPrereqError(
2260
      op, "Unsupported disk template conversion from .* to .*")
2261

    
2262
  def testConvertToDRBDWithSecondarySameAsPrimary(self):
2263
    op = self.CopyOpCode(self.op,
2264
                         disk_template=constants.DT_DRBD8,
2265
                         remote_node=self.master.name)
2266
    self.ExecOpCodeExpectOpPrereqError(
2267
      op, "Given new secondary node .* is the same as the primary node"
2268
          " of the instance")
2269

    
2270
  def testConvertPlainToDRBD(self):
2271
    self.rpc.call_blockdev_shutdown.return_value = \
2272
      self.RpcResultsBuilder() \
2273
        .CreateSuccessfulNodeResult(self.master, True)
2274
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2275
      self.RpcResultsBuilder() \
2276
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2277

    
2278
    op = self.CopyOpCode(self.op,
2279
                         disk_template=constants.DT_DRBD8,
2280
                         remote_node=self.snode.name)
2281
    self.ExecOpCode(op)
2282

    
2283
  def testConvertDRBDToPlain(self):
2284
    self.inst.disks = [self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
2285
                                           primary_node=self.master,
2286
                                           secondary_node=self.snode)]
2287
    self.inst.disk_template = constants.DT_DRBD8
2288
    self.rpc.call_blockdev_shutdown.return_value = \
2289
      self.RpcResultsBuilder() \
2290
        .CreateSuccessfulNodeResult(self.master, True)
2291
    self.rpc.call_blockdev_remove.return_value = \
2292
      self.RpcResultsBuilder() \
2293
        .CreateSuccessfulNodeResult(self.master)
2294
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2295
      self.RpcResultsBuilder() \
2296
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2297

    
2298
    op = self.CopyOpCode(self.op,
2299
                         disk_template=constants.DT_PLAIN)
2300
    self.ExecOpCode(op)
2301

    
2302

    
2303
class TestLUInstanceChangeGroup(CmdlibTestCase):
2304
  def setUp(self):
2305
    super(TestLUInstanceChangeGroup, self).setUp()
2306

    
2307
    self.group2 = self.cfg.AddNewNodeGroup()
2308
    self.node2 = self.cfg.AddNewNode(group=self.group2)
2309
    self.inst = self.cfg.AddNewInstance()
2310
    self.op = opcodes.OpInstanceChangeGroup(instance_name=self.inst.name)
2311

    
2312
  def testTargetGroupIsInstanceGroup(self):
2313
    op = self.CopyOpCode(self.op,
2314
                         target_groups=[self.group.name])
2315
    self.ExecOpCodeExpectOpPrereqError(
2316
      op, "Can't use group\(s\) .* as targets, they are used by the"
2317
          " instance .*")
2318

    
2319
  def testNoTargetGroups(self):
2320
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
2321
                                   primary_node=self.master,
2322
                                   secondary_node=self.node2)
2323
    op = self.CopyOpCode(self.op,
2324
                         instance_name=inst.name)
2325
    self.ExecOpCodeExpectOpPrereqError(
2326
      op, "There are no possible target groups")
2327

    
2328
  def testFailingIAllocator(self):
2329
    self.iallocator_cls.return_value.success = False
2330
    op = self.CopyOpCode(self.op)
2331

    
2332
    self.ExecOpCodeExpectOpPrereqError(
2333
      op, "Can't compute solution for changing group of instance .*"
2334
          " using iallocator .*")
2335

    
2336
  def testChangeGroup(self):
2337
    self.iallocator_cls.return_value.success = True
2338
    self.iallocator_cls.return_value.result = ([], [], [])
2339
    op = self.CopyOpCode(self.op)
2340

    
2341
    self.ExecOpCode(op)
2342

    
2343

    
2344
if __name__ == "__main__":
2345
  testutils.GanetiTestProgram()