Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (83.8 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 testVlanWithWrongMode(self):
184
    op = self.CopyOpCode(self.diskless_op,
185
                         nics=[{
186
                           constants.INIC_VLAN: ":1",
187
                           constants.INIC_MODE: constants.NIC_MODE_BRIDGED
188
                         }])
189
    self.ExecOpCodeExpectOpPrereqError(
190
      op, "VLAN is given, but network mode is not openvswitch")
191

    
192
  def testAutoIpNoNameCheck(self):
193
    op = self.CopyOpCode(self.diskless_op,
194
                         nics=[{
195
                           constants.INIC_IP: constants.VALUE_AUTO
196
                         }],
197
                         ip_check=False,
198
                         name_check=False)
199
    self.ExecOpCodeExpectOpPrereqError(
200
      op, "IP address set to auto but name checks have been skipped")
201

    
202
  def testAutoIp(self):
203
    op = self.CopyOpCode(self.diskless_op,
204
                         nics=[{
205
                           constants.INIC_IP: constants.VALUE_AUTO
206
                         }])
207
    self.ExecOpCode(op)
208

    
209
  def testPoolIpNoNetwork(self):
210
    op = self.CopyOpCode(self.diskless_op,
211
                         nics=[{
212
                           constants.INIC_IP: constants.NIC_IP_POOL
213
                         }])
214
    self.ExecOpCodeExpectOpPrereqError(
215
      op, "if ip=pool, parameter network must be passed too")
216

    
217
  def testValidIp(self):
218
    op = self.CopyOpCode(self.diskless_op,
219
                         nics=[{
220
                           constants.INIC_IP: "203.0.113.1"
221
                         }])
222
    self.ExecOpCode(op)
223

    
224
  def testRoutedNoIp(self):
225
    op = self.CopyOpCode(self.diskless_op,
226
                         nics=[{
227
                           constants.INIC_MODE: constants.NIC_MODE_ROUTED
228
                         }])
229
    self.ExecOpCodeExpectOpPrereqError(
230
      op, "Routed nic mode requires an ip address")
231

    
232
  def testValicMac(self):
233
    op = self.CopyOpCode(self.diskless_op,
234
                         nics=[{
235
                           constants.INIC_MAC: "f0:df:f4:a3:d1:cf"
236
                         }])
237
    self.ExecOpCode(op)
238

    
239
  def testValidNicParams(self):
240
    op = self.CopyOpCode(self.diskless_op,
241
                         nics=[{
242
                           constants.INIC_MODE: constants.NIC_MODE_BRIDGED,
243
                           constants.INIC_LINK: "br_mock"
244
                         }])
245
    self.ExecOpCode(op)
246

    
247
  def testValidNicParamsOpenVSwitch(self):
248
    op = self.CopyOpCode(self.diskless_op,
249
                         nics=[{
250
                           constants.INIC_MODE: constants.NIC_MODE_OVS,
251
                           constants.INIC_VLAN: "1"
252
                         }])
253
    self.ExecOpCode(op)
254

    
255
  def testNicNoneName(self):
256
    op = self.CopyOpCode(self.diskless_op,
257
                         nics=[{
258
                           constants.INIC_NAME: constants.VALUE_NONE
259
                         }])
260
    self.ExecOpCode(op)
261

    
262
  def testConflictingIP(self):
263
    op = self.CopyOpCode(self.diskless_op,
264
                         nics=[{
265
                           constants.INIC_IP: self.net.gateway[:-1] + "2"
266
                         }])
267
    self.ExecOpCodeExpectOpPrereqError(
268
      op, "The requested IP address .* belongs to network .*, but the target"
269
          " NIC does not.")
270

    
271
  def testVLanFormat(self):
272
    for vlan in [".pinky", ":bunny", ":1:pinky", "bunny"]:
273
      self.ResetMocks()
274
      op = self.CopyOpCode(self.diskless_op,
275
                           nics=[{
276
                             constants.INIC_VLAN: vlan
277
                           }])
278
      self.ExecOpCodeExpectOpPrereqError(
279
        op, "Specified VLAN parameter is invalid")
280

    
281
  def testPoolIp(self):
282
    op = self.CopyOpCode(self.diskless_op,
283
                         nics=[{
284
                           constants.INIC_IP: constants.NIC_IP_POOL,
285
                           constants.INIC_NETWORK: self.net.name
286
                         }])
287
    self.ExecOpCode(op)
288

    
289
  def testPoolIpUnconnectedNetwork(self):
290
    net = self.cfg.AddNewNetwork()
291
    op = self.CopyOpCode(self.diskless_op,
292
                         nics=[{
293
                           constants.INIC_IP: constants.NIC_IP_POOL,
294
                           constants.INIC_NETWORK: net.name
295
                         }])
296
    self.ExecOpCodeExpectOpPrereqError(
297
      op, "No netparams found for network .*.")
298

    
299
  def testIpNotInNetwork(self):
300
    op = self.CopyOpCode(self.diskless_op,
301
                         nics=[{
302
                           constants.INIC_IP: "203.0.113.1",
303
                           constants.INIC_NETWORK: self.net.name
304
                         }])
305
    self.ExecOpCodeExpectOpPrereqError(
306
      op, "IP address .* already in use or does not belong to network .*")
307

    
308
  def testMixAdoptAndNotAdopt(self):
309
    op = self.CopyOpCode(self.diskless_op,
310
                         disk_template=constants.DT_PLAIN,
311
                         disks=[{
312
                           constants.IDISK_ADOPT: "lv1"
313
                         }, {}])
314
    self.ExecOpCodeExpectOpPrereqError(
315
      op, "Either all disks are adopted or none is")
316

    
317
  def testMustAdoptWithoutAdopt(self):
318
    op = self.CopyOpCode(self.diskless_op,
319
                         disk_template=constants.DT_BLOCK,
320
                         disks=[{}])
321
    self.ExecOpCodeExpectOpPrereqError(
322
      op, "Disk template blockdev requires disk adoption, but no 'adopt'"
323
          " parameter given")
324

    
325
  def testDontAdoptWithAdopt(self):
326
    op = self.CopyOpCode(self.diskless_op,
327
                         disk_template=constants.DT_DRBD8,
328
                         disks=[{
329
                           constants.IDISK_ADOPT: "lv1"
330
                         }])
331
    self.ExecOpCodeExpectOpPrereqError(
332
      op, "Disk adoption is not supported for the 'drbd' disk template")
333

    
334
  def testAdoptWithIAllocator(self):
335
    op = self.CopyOpCode(self.diskless_op,
336
                         disk_template=constants.DT_PLAIN,
337
                         disks=[{
338
                           constants.IDISK_ADOPT: "lv1"
339
                         }],
340
                         iallocator="mock")
341
    self.ExecOpCodeExpectOpPrereqError(
342
      op, "Disk adoption not allowed with an iallocator script")
343

    
344
  def testAdoptWithImport(self):
345
    op = self.CopyOpCode(self.diskless_op,
346
                         disk_template=constants.DT_PLAIN,
347
                         disks=[{
348
                           constants.IDISK_ADOPT: "lv1"
349
                         }],
350
                         mode=constants.INSTANCE_IMPORT)
351
    self.ExecOpCodeExpectOpPrereqError(
352
      op, "Disk adoption not allowed for instance import")
353

    
354
  def testArgumentCombinations(self):
355
    op = self.CopyOpCode(self.diskless_op,
356
                         # start flag will be flipped
357
                         no_install=True,
358
                         start=True,
359
                         # no allowed combination
360
                         ip_check=True,
361
                         name_check=False)
362
    self.ExecOpCodeExpectOpPrereqError(
363
      op, "Cannot do IP address check without a name check")
364

    
365
  def testInvalidFileDriver(self):
366
    op = self.CopyOpCode(self.diskless_op,
367
                         file_driver="invalid_file_driver")
368
    self.ExecOpCodeExpectOpPrereqError(
369
      op, "Parameter 'OP_INSTANCE_CREATE.file_driver' fails validation")
370

    
371
  def testMissingSecondaryNode(self):
372
    op = self.CopyOpCode(self.diskless_op,
373
                         pnode=self.master.name,
374
                         disk_template=constants.DT_DRBD8)
375
    self.ExecOpCodeExpectOpPrereqError(
376
      op, "The networked disk templates need a mirror node")
377

    
378
  def testIgnoredSecondaryNode(self):
379
    op = self.CopyOpCode(self.diskless_op,
380
                         pnode=self.master.name,
381
                         snode=self.node1.name,
382
                         disk_template=constants.DT_PLAIN)
383
    try:
384
      self.ExecOpCode(op)
385
    except Exception:
386
      pass
387
    self.mcpu.assertLogContainsRegex(
388
      "Secondary node will be ignored on non-mirrored disk template")
389

    
390
  def testMissingOsType(self):
391
    op = self.CopyOpCode(self.diskless_op,
392
                         os_type=self.REMOVE)
393
    self.ExecOpCodeExpectOpPrereqError(op, "No guest OS specified")
394

    
395
  def testBlacklistedOs(self):
396
    self.cluster.blacklisted_os = [self.os_name_variant]
397
    op = self.CopyOpCode(self.diskless_op)
398
    self.ExecOpCodeExpectOpPrereqError(
399
      op, "Guest OS .* is not allowed for installation")
400

    
401
  def testMissingDiskTemplate(self):
402
    self.cluster.enabled_disk_templates = [constants.DT_DISKLESS]
403
    op = self.CopyOpCode(self.diskless_op,
404
                         disk_template=self.REMOVE)
405
    self.ExecOpCode(op)
406

    
407
  def testExistingInstance(self):
408
    inst = self.cfg.AddNewInstance()
409
    op = self.CopyOpCode(self.diskless_op,
410
                         instance_name=inst.name)
411
    self.ExecOpCodeExpectOpPrereqError(
412
      op, "Instance .* is already in the cluster")
413

    
414
  def testPlainInstance(self):
415
    op = self.CopyOpCode(self.plain_op)
416
    self.ExecOpCode(op)
417

    
418
  def testPlainIAllocator(self):
419
    op = self.CopyOpCode(self.plain_op,
420
                         pnode=self.REMOVE,
421
                         iallocator="mock")
422
    self.ExecOpCode(op)
423

    
424
  def testIAllocatorOpportunisticLocking(self):
425
    op = self.CopyOpCode(self.plain_op,
426
                         pnode=self.REMOVE,
427
                         iallocator="mock",
428
                         opportunistic_locking=True)
429
    self.ExecOpCode(op)
430

    
431
  def testFailingIAllocator(self):
432
    self.iallocator_cls.return_value.success = False
433
    op = self.CopyOpCode(self.plain_op,
434
                         pnode=self.REMOVE,
435
                         iallocator="mock")
436
    self.ExecOpCodeExpectOpPrereqError(
437
      op, "Can't compute nodes using iallocator")
438

    
439
  def testDrbdInstance(self):
440
    op = self.CopyOpCode(self.drbd_op)
441
    self.ExecOpCode(op)
442

    
443
  def testDrbdIAllocator(self):
444
    op = self.CopyOpCode(self.drbd_op,
445
                         pnode=self.REMOVE,
446
                         snode=self.REMOVE,
447
                         iallocator="mock")
448
    self.ExecOpCode(op)
449

    
450
  def testFileInstance(self):
451
    op = self.CopyOpCode(self.file_op)
452
    self.ExecOpCode(op)
453

    
454
  def testFileInstanceNoClusterStorage(self):
455
    self.cluster.file_storage_dir = None
456
    op = self.CopyOpCode(self.file_op)
457
    self.ExecOpCodeExpectOpPrereqError(
458
      op, "Cluster file storage dir not defined")
459

    
460
  def testFileInstanceAdditionalPath(self):
461
    op = self.CopyOpCode(self.file_op,
462
                         file_storage_dir="mock_dir")
463
    self.ExecOpCode(op)
464

    
465
  def testIdentifyDefaults(self):
466
    op = self.CopyOpCode(self.plain_op,
467
                         hvparams={
468
                           constants.HV_BOOT_ORDER: "cd"
469
                         },
470
                         beparams=constants.BEC_DEFAULTS.copy(),
471
                         nics=[{
472
                           constants.NIC_MODE: constants.NIC_MODE_BRIDGED
473
                         }],
474
                         osparams={
475
                           self.os_name_variant: {}
476
                         },
477
                         identify_defaults=True)
478
    self.ExecOpCode(op)
479

    
480
    inst = self.cfg.GetAllInstancesInfo().values()[0]
481
    self.assertEqual(0, len(inst.hvparams))
482
    self.assertEqual(0, len(inst.beparams))
483
    assert self.os_name_variant not in inst.osparams or \
484
            len(inst.osparams[self.os_name_variant]) == 0
485

    
486
  def testOfflineNode(self):
487
    self.node1.offline = True
488
    op = self.CopyOpCode(self.diskless_op,
489
                         pnode=self.node1.name)
490
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot use offline primary node")
491

    
492
  def testDrainedNode(self):
493
    self.node1.drained = True
494
    op = self.CopyOpCode(self.diskless_op,
495
                         pnode=self.node1.name)
496
    self.ExecOpCodeExpectOpPrereqError(op, "Cannot use drained primary node")
497

    
498
  def testNonVmCapableNode(self):
499
    self.node1.vm_capable = False
500
    op = self.CopyOpCode(self.diskless_op,
501
                         pnode=self.node1.name)
502
    self.ExecOpCodeExpectOpPrereqError(
503
      op, "Cannot use non-vm_capable primary node")
504

    
505
  def testNonEnabledHypervisor(self):
506
    self.cluster.enabled_hypervisors = [constants.HT_XEN_HVM]
507
    op = self.CopyOpCode(self.diskless_op,
508
                         hypervisor=constants.HT_FAKE)
509
    self.ExecOpCodeExpectOpPrereqError(
510
      op, "Selected hypervisor .* not enabled in the cluster")
511

    
512
  def testAddTag(self):
513
    op = self.CopyOpCode(self.diskless_op,
514
                         tags=["tag"])
515
    self.ExecOpCode(op)
516

    
517
  def testInvalidTag(self):
518
    op = self.CopyOpCode(self.diskless_op,
519
                         tags=["too_long" * 20])
520
    self.ExecOpCodeExpectException(op, errors.TagError, "Tag too long")
521

    
522
  def testPingableInstanceName(self):
523
    self.netutils_mod.TcpPing.return_value = True
524
    op = self.CopyOpCode(self.diskless_op)
525
    self.ExecOpCodeExpectOpPrereqError(
526
      op, "IP .* of instance diskless.test.com already in use")
527

    
528
  def testPrimaryIsSecondaryNode(self):
529
    op = self.CopyOpCode(self.drbd_op,
530
                         snode=self.drbd_op.pnode)
531
    self.ExecOpCodeExpectOpPrereqError(
532
      op, "The secondary node cannot be the primary node")
533

    
534
  def testPrimarySecondaryDifferentNodeGroups(self):
535
    group = self.cfg.AddNewNodeGroup()
536
    self.node2.group = group.uuid
537
    op = self.CopyOpCode(self.drbd_op)
538
    self.ExecOpCode(op)
539
    self.mcpu.assertLogContainsRegex(
540
      "The primary and secondary nodes are in two different node groups")
541

    
542
  def testExclusiveStorageUnsupportedDiskTemplate(self):
543
    self.node1.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
544
    op = self.CopyOpCode(self.drbd_op)
545
    self.ExecOpCodeExpectOpPrereqError(
546
      op, "Disk template drbd not supported with exclusive storage")
547

    
548
  def testAdoptPlain(self):
549
    self.rpc.call_lv_list.return_value = \
550
      self.RpcResultsBuilder() \
551
        .AddSuccessfulNode(self.master, {
552
          "xenvg/mock_disk_1": (10000, None, False)
553
        }) \
554
        .Build()
555
    op = self.CopyOpCode(self.plain_op)
556
    op.disks[0].update({constants.IDISK_ADOPT: "mock_disk_1"})
557
    self.ExecOpCode(op)
558

    
559
  def testAdoptPlainMissingLv(self):
560
    self.rpc.call_lv_list.return_value = \
561
      self.RpcResultsBuilder() \
562
        .AddSuccessfulNode(self.master, {}) \
563
        .Build()
564
    op = self.CopyOpCode(self.plain_op)
565
    op.disks[0].update({constants.IDISK_ADOPT: "mock_disk_1"})
566
    self.ExecOpCodeExpectOpPrereqError(op, "Missing logical volume")
567

    
568
  def testAdoptPlainOnlineLv(self):
569
    self.rpc.call_lv_list.return_value = \
570
      self.RpcResultsBuilder() \
571
        .AddSuccessfulNode(self.master, {
572
          "xenvg/mock_disk_1": (10000, None, True)
573
        }) \
574
        .Build()
575
    op = self.CopyOpCode(self.plain_op)
576
    op.disks[0].update({constants.IDISK_ADOPT: "mock_disk_1"})
577
    self.ExecOpCodeExpectOpPrereqError(
578
      op, "Online logical volumes found, cannot adopt")
579

    
580
  def testAdoptBlock(self):
581
    self.rpc.call_bdev_sizes.return_value = \
582
      self.RpcResultsBuilder() \
583
        .AddSuccessfulNode(self.master, {
584
          "/dev/disk/block0": 10000
585
        }) \
586
        .Build()
587
    op = self.CopyOpCode(self.block_op)
588
    self.ExecOpCode(op)
589

    
590
  def testAdoptBlockDuplicateNames(self):
591
    op = self.CopyOpCode(self.block_op,
592
                         disks=[{
593
                           constants.IDISK_SIZE: 0,
594
                           constants.IDISK_ADOPT: "/dev/disk/block0"
595
                         }, {
596
                           constants.IDISK_SIZE: 0,
597
                           constants.IDISK_ADOPT: "/dev/disk/block0"
598
                         }])
599
    self.ExecOpCodeExpectOpPrereqError(
600
      op, "Duplicate disk names given for adoption")
601

    
602
  def testAdoptBlockInvalidNames(self):
603
    op = self.CopyOpCode(self.block_op,
604
                         disks=[{
605
                           constants.IDISK_SIZE: 0,
606
                           constants.IDISK_ADOPT: "/invalid/block0"
607
                         }])
608
    self.ExecOpCodeExpectOpPrereqError(
609
      op, "Device node.* lie outside .* and cannot be adopted")
610

    
611
  def testAdoptBlockMissingDisk(self):
612
    self.rpc.call_bdev_sizes.return_value = \
613
      self.RpcResultsBuilder() \
614
        .AddSuccessfulNode(self.master, {}) \
615
        .Build()
616
    op = self.CopyOpCode(self.block_op)
617
    self.ExecOpCodeExpectOpPrereqError(op, "Missing block device")
618

    
619
  def testNoWaitForSyncDrbd(self):
620
    op = self.CopyOpCode(self.drbd_op,
621
                         wait_for_sync=False)
622
    self.ExecOpCode(op)
623

    
624
  def testNoWaitForSyncPlain(self):
625
    op = self.CopyOpCode(self.plain_op,
626
                         wait_for_sync=False)
627
    self.ExecOpCode(op)
628

    
629
  def testImportPlainFromGivenSrcNode(self):
630
    exp_info = """
631
[export]
632
version=0
633
os=mock_os
634
[instance]
635
name=old_name.example.com
636
"""
637

    
638
    self.rpc.call_export_info.return_value = \
639
      self.RpcResultsBuilder() \
640
        .CreateSuccessfulNodeResult(self.master, exp_info)
641
    op = self.CopyOpCode(self.plain_op,
642
                         mode=constants.INSTANCE_IMPORT,
643
                         src_node=self.master.name)
644
    self.ExecOpCode(op)
645

    
646
  def testImportPlainWithoutSrcNodeNotFound(self):
647
    op = self.CopyOpCode(self.plain_op,
648
                         mode=constants.INSTANCE_IMPORT)
649
    self.ExecOpCodeExpectOpPrereqError(
650
      op, "No export found for relative path")
651

    
652
  def testImportPlainWithoutSrcNode(self):
653
    exp_info = """
654
[export]
655
version=0
656
os=mock_os
657
[instance]
658
name=old_name.example.com
659
"""
660

    
661
    self.rpc.call_export_list.return_value = \
662
      self.RpcResultsBuilder() \
663
        .AddSuccessfulNode(self.master, {"mock_path": {}}) \
664
        .Build()
665
    self.rpc.call_export_info.return_value = \
666
      self.RpcResultsBuilder() \
667
        .CreateSuccessfulNodeResult(self.master, exp_info)
668

    
669
    op = self.CopyOpCode(self.plain_op,
670
                         mode=constants.INSTANCE_IMPORT,
671
                         src_path="mock_path")
672
    self.ExecOpCode(op)
673

    
674
  def testImportPlainCorruptExportInfo(self):
675
    exp_info = ""
676
    self.rpc.call_export_info.return_value = \
677
      self.RpcResultsBuilder() \
678
        .CreateSuccessfulNodeResult(self.master, exp_info)
679
    op = self.CopyOpCode(self.plain_op,
680
                         mode=constants.INSTANCE_IMPORT,
681
                         src_node=self.master.name)
682
    self.ExecOpCodeExpectException(op, errors.ProgrammerError,
683
                                   "Corrupted export config")
684

    
685
  def testImportPlainWrongExportInfoVersion(self):
686
    exp_info = """
687
[export]
688
version=1
689
"""
690
    self.rpc.call_export_info.return_value = \
691
      self.RpcResultsBuilder() \
692
        .CreateSuccessfulNodeResult(self.master, exp_info)
693
    op = self.CopyOpCode(self.plain_op,
694
                         mode=constants.INSTANCE_IMPORT,
695
                         src_node=self.master.name)
696
    self.ExecOpCodeExpectOpPrereqError(op, "Wrong export version")
697

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

    
723
    self.rpc.call_export_info.return_value = \
724
      self.RpcResultsBuilder() \
725
        .CreateSuccessfulNodeResult(self.master, exp_info)
726
    self.rpc.call_import_start.return_value = \
727
      self.RpcResultsBuilder() \
728
        .CreateSuccessfulNodeResult(self.master, "daemon_name")
729
    self.rpc.call_impexp_status.return_value = \
730
      self.RpcResultsBuilder() \
731
        .CreateSuccessfulNodeResult(self.master,
732
                                    [
733
                                      objects.ImportExportStatus(exit_status=0)
734
                                    ])
735
    self.rpc.call_impexp_cleanup.return_value = \
736
      self.RpcResultsBuilder() \
737
        .CreateSuccessfulNodeResult(self.master, True)
738

    
739
    op = self.CopyOpCode(self.plain_op,
740
                         disks=[],
741
                         nics=[],
742
                         tags=[],
743
                         hypervisor=None,
744
                         hvparams={},
745
                         mode=constants.INSTANCE_IMPORT,
746
                         src_node=self.master.name)
747
    self.ExecOpCode(op)
748

    
749

    
750
class TestCheckOSVariant(CmdlibTestCase):
751
  def testNoVariantsSupported(self):
752
    os = self.cfg.CreateOs(supported_variants=[])
753
    self.assertRaises(errors.OpPrereqError, instance._CheckOSVariant,
754
                      os, "os+variant")
755

    
756
  def testNoVariantGiven(self):
757
    os = self.cfg.CreateOs(supported_variants=["default"])
758
    self.assertRaises(errors.OpPrereqError, instance._CheckOSVariant,
759
                      os, "os")
760

    
761
  def testWrongVariantGiven(self):
762
    os = self.cfg.CreateOs(supported_variants=["default"])
763
    self.assertRaises(errors.OpPrereqError, instance._CheckOSVariant,
764
                      os, "os+wrong_variant")
765

    
766
  def testOkWithVariant(self):
767
    os = self.cfg.CreateOs(supported_variants=["default"])
768
    instance._CheckOSVariant(os, "os+default")
769

    
770
  def testOkWithoutVariant(self):
771
    os = self.cfg.CreateOs(supported_variants=[])
772
    instance._CheckOSVariant(os, "os")
773

    
774

    
775
class TestCheckTargetNodeIPolicy(TestLUInstanceCreate):
776
  def setUp(self):
777
    super(TestCheckTargetNodeIPolicy, self).setUp()
778

    
779
    self.op = self.diskless_op
780

    
781
    self.instance = self.cfg.AddNewInstance()
782
    self.target_group = self.cfg.AddNewNodeGroup()
783
    self.target_node = self.cfg.AddNewNode(group=self.target_group)
784

    
785
  @withLockedLU
786
  def testNoViolation(self, lu):
787
    compute_recoder = mock.Mock(return_value=[])
788
    instance.CheckTargetNodeIPolicy(lu, NotImplemented, self.instance,
789
                                    self.target_node, NotImplemented,
790
                                    _compute_fn=compute_recoder)
791
    self.assertTrue(compute_recoder.called)
792
    self.mcpu.assertLogIsEmpty()
793

    
794
  @withLockedLU
795
  def testNoIgnore(self, lu):
796
    compute_recoder = mock.Mock(return_value=["mem_size not in range"])
797
    self.assertRaises(errors.OpPrereqError, instance.CheckTargetNodeIPolicy,
798
                      lu, NotImplemented, self.instance,
799
                      self.target_node, NotImplemented,
800
                      _compute_fn=compute_recoder)
801
    self.assertTrue(compute_recoder.called)
802
    self.mcpu.assertLogIsEmpty()
803

    
804
  @withLockedLU
805
  def testIgnoreViolation(self, lu):
806
    compute_recoder = mock.Mock(return_value=["mem_size not in range"])
807
    instance.CheckTargetNodeIPolicy(lu, NotImplemented, self.instance,
808
                                    self.target_node, NotImplemented,
809
                                    ignore=True, _compute_fn=compute_recoder)
810
    self.assertTrue(compute_recoder.called)
811
    msg = ("Instance does not meet target node group's .* instance policy:"
812
           " mem_size not in range")
813
    self.mcpu.assertLogContainsRegex(msg)
814

    
815

    
816
class TestApplyContainerMods(unittest.TestCase):
817
  def testEmptyContainer(self):
818
    container = []
819
    chgdesc = []
820
    instance._ApplyContainerMods("test", container, chgdesc, [], None, None,
821
                                 None)
822
    self.assertEqual(container, [])
823
    self.assertEqual(chgdesc, [])
824

    
825
  def testAdd(self):
826
    container = []
827
    chgdesc = []
828
    mods = instance._PrepareContainerMods([
829
      (constants.DDM_ADD, -1, "Hello"),
830
      (constants.DDM_ADD, -1, "World"),
831
      (constants.DDM_ADD, 0, "Start"),
832
      (constants.DDM_ADD, -1, "End"),
833
      ], None)
834
    instance._ApplyContainerMods("test", container, chgdesc, mods,
835
                                 None, None, None)
836
    self.assertEqual(container, ["Start", "Hello", "World", "End"])
837
    self.assertEqual(chgdesc, [])
838

    
839
    mods = instance._PrepareContainerMods([
840
      (constants.DDM_ADD, 0, "zero"),
841
      (constants.DDM_ADD, 3, "Added"),
842
      (constants.DDM_ADD, 5, "four"),
843
      (constants.DDM_ADD, 7, "xyz"),
844
      ], None)
845
    instance._ApplyContainerMods("test", container, chgdesc, mods,
846
                                 None, None, None)
847
    self.assertEqual(container,
848
                     ["zero", "Start", "Hello", "Added", "World", "four",
849
                      "End", "xyz"])
850
    self.assertEqual(chgdesc, [])
851

    
852
    for idx in [-2, len(container) + 1]:
853
      mods = instance._PrepareContainerMods([
854
        (constants.DDM_ADD, idx, "error"),
855
        ], None)
856
      self.assertRaises(IndexError, instance._ApplyContainerMods,
857
                        "test", container, None, mods, None, None, None)
858

    
859
  def testRemoveError(self):
860
    for idx in [0, 1, 2, 100, -1, -4]:
861
      mods = instance._PrepareContainerMods([
862
        (constants.DDM_REMOVE, idx, None),
863
        ], None)
864
      self.assertRaises(IndexError, instance._ApplyContainerMods,
865
                        "test", [], None, mods, None, None, None)
866

    
867
    mods = instance._PrepareContainerMods([
868
      (constants.DDM_REMOVE, 0, object()),
869
      ], None)
870
    self.assertRaises(AssertionError, instance._ApplyContainerMods,
871
                      "test", [""], None, mods, None, None, None)
872

    
873
  def testAddError(self):
874
    for idx in range(-100, -1) + [100]:
875
      mods = instance._PrepareContainerMods([
876
        (constants.DDM_ADD, idx, None),
877
        ], None)
878
      self.assertRaises(IndexError, instance._ApplyContainerMods,
879
                        "test", [], None, mods, None, None, None)
880

    
881
  def testRemove(self):
882
    container = ["item 1", "item 2"]
883
    mods = instance._PrepareContainerMods([
884
      (constants.DDM_ADD, -1, "aaa"),
885
      (constants.DDM_REMOVE, -1, None),
886
      (constants.DDM_ADD, -1, "bbb"),
887
      ], None)
888
    chgdesc = []
889
    instance._ApplyContainerMods("test", container, chgdesc, mods,
890
                                 None, None, None)
891
    self.assertEqual(container, ["item 1", "item 2", "bbb"])
892
    self.assertEqual(chgdesc, [
893
      ("test/2", "remove"),
894
      ])
895

    
896
  def testModify(self):
897
    container = ["item 1", "item 2"]
898
    mods = instance._PrepareContainerMods([
899
      (constants.DDM_MODIFY, -1, "a"),
900
      (constants.DDM_MODIFY, 0, "b"),
901
      (constants.DDM_MODIFY, 1, "c"),
902
      ], None)
903
    chgdesc = []
904
    instance._ApplyContainerMods("test", container, chgdesc, mods,
905
                                 None, None, None)
906
    self.assertEqual(container, ["item 1", "item 2"])
907
    self.assertEqual(chgdesc, [])
908

    
909
    for idx in [-2, len(container) + 1]:
910
      mods = instance._PrepareContainerMods([
911
        (constants.DDM_MODIFY, idx, "error"),
912
        ], None)
913
      self.assertRaises(IndexError, instance._ApplyContainerMods,
914
                        "test", container, None, mods, None, None, None)
915

    
916
  @staticmethod
917
  def _CreateTestFn(idx, params, private):
918
    private.data = ("add", idx, params)
919
    return ((100 * idx, params), [
920
      ("test/%s" % idx, hex(idx)),
921
      ])
922

    
923
  @staticmethod
924
  def _ModifyTestFn(idx, item, params, private):
925
    private.data = ("modify", idx, params)
926
    return [
927
      ("test/%s" % idx, "modify %s" % params),
928
      ]
929

    
930
  @staticmethod
931
  def _RemoveTestFn(idx, item, private):
932
    private.data = ("remove", idx, item)
933

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

    
978

    
979
class _FakeConfigForGenDiskTemplate(ConfigMock):
980
  def __init__(self):
981
    super(_FakeConfigForGenDiskTemplate, self).__init__()
982

    
983
    self._unique_id = itertools.count()
984
    self._drbd_minor = itertools.count(20)
985
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
986
    self._secret = itertools.count()
987

    
988
  def GenerateUniqueID(self, ec_id):
989
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
990

    
991
  def AllocateDRBDMinor(self, nodes, instance):
992
    return [self._drbd_minor.next()
993
            for _ in nodes]
994

    
995
  def AllocatePort(self):
996
    return self._port.next()
997

    
998
  def GenerateDRBDSecret(self, ec_id):
999
    return "ec%s-secret%s" % (ec_id, self._secret.next())
1000

    
1001

    
1002
class TestGenerateDiskTemplate(CmdlibTestCase):
1003
  def setUp(self):
1004
    super(TestGenerateDiskTemplate, self).setUp()
1005

    
1006
    self.cfg = _FakeConfigForGenDiskTemplate()
1007
    self.cluster.enabled_disk_templates = list(constants.DISK_TEMPLATES)
1008

    
1009
    self.nodegroup = self.cfg.AddNewNodeGroup(name="ng")
1010

    
1011
    self.lu = self.GetMockLU()
1012

    
1013
  @staticmethod
1014
  def GetDiskParams():
1015
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1016

    
1017
  def testWrongDiskTemplate(self):
1018
    gdt = instance.GenerateDiskTemplate
1019
    disk_template = "##unknown##"
1020

    
1021
    assert disk_template not in constants.DISK_TEMPLATES
1022

    
1023
    self.assertRaises(errors.OpPrereqError, gdt, self.lu, disk_template,
1024
                      "inst26831.example.com", "node30113.example.com", [], [],
1025
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1026
                      self.GetDiskParams())
1027

    
1028
  def testDiskless(self):
1029
    gdt = instance.GenerateDiskTemplate
1030

    
1031
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1032
                 "node30113.example.com", [], [],
1033
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1034
                 self.GetDiskParams())
1035
    self.assertEqual(result, [])
1036

    
1037
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1038
                       file_storage_dir=NotImplemented,
1039
                       file_driver=NotImplemented):
1040
    gdt = instance.GenerateDiskTemplate
1041

    
1042
    map(lambda params: utils.ForceDictType(params,
1043
                                           constants.IDISK_PARAMS_TYPES),
1044
        disk_info)
1045

    
1046
    # Check if non-empty list of secondaries is rejected
1047
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1048
                      template, "inst25088.example.com",
1049
                      "node185.example.com", ["node323.example.com"], [],
1050
                      NotImplemented, NotImplemented, base_index,
1051
                      self.lu.LogInfo, self.GetDiskParams())
1052

    
1053
    result = gdt(self.lu, template, "inst21662.example.com",
1054
                 "node21741.example.com", [],
1055
                 disk_info, file_storage_dir, file_driver, base_index,
1056
                 self.lu.LogInfo, self.GetDiskParams())
1057

    
1058
    for (idx, disk) in enumerate(result):
1059
      self.assertTrue(isinstance(disk, objects.Disk))
1060
      self.assertEqual(disk.dev_type, exp_dev_type)
1061
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1062
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1063
      self.assertTrue(disk.children is None)
1064

    
1065
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1066
    instance._UpdateIvNames(base_index, result)
1067
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1068

    
1069
    return result
1070

    
1071
  def _CheckIvNames(self, disks, base_index, end_index):
1072
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1073
                     ["disk/%s" % i for i in range(base_index, end_index)])
1074

    
1075
  def testPlain(self):
1076
    disk_info = [{
1077
      constants.IDISK_SIZE: 1024,
1078
      constants.IDISK_MODE: constants.DISK_RDWR,
1079
      }, {
1080
      constants.IDISK_SIZE: 4096,
1081
      constants.IDISK_VG: "othervg",
1082
      constants.IDISK_MODE: constants.DISK_RDWR,
1083
      }]
1084

    
1085
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1086
                                   constants.DT_PLAIN)
1087

    
1088
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1089
      ("xenvg", "ec1-uq0.disk3"),
1090
      ("othervg", "ec1-uq1.disk4"),
1091
      ])
1092

    
1093
  def testFile(self):
1094
    # anything != DT_FILE would do here
1095
    self.cluster.enabled_disk_templates = [constants.DT_PLAIN]
1096
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1097
                      constants.DT_FILE, [], 0, NotImplemented)
1098
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1099
                      constants.DT_SHARED_FILE, [], 0, NotImplemented)
1100

    
1101
    for disk_template in constants.DTS_FILEBASED:
1102
      disk_info = [{
1103
        constants.IDISK_SIZE: 80 * 1024,
1104
        constants.IDISK_MODE: constants.DISK_RDONLY,
1105
        }, {
1106
        constants.IDISK_SIZE: 4096,
1107
        constants.IDISK_MODE: constants.DISK_RDWR,
1108
        }, {
1109
        constants.IDISK_SIZE: 6 * 1024,
1110
        constants.IDISK_MODE: constants.DISK_RDWR,
1111
        }]
1112

    
1113
      self.cluster.enabled_disk_templates = [disk_template]
1114
      result = self._TestTrivialDisk(
1115
        disk_template, disk_info, 2, disk_template,
1116
        file_storage_dir="/tmp", file_driver=constants.FD_BLKTAP)
1117

    
1118
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1119
        (constants.FD_BLKTAP, "/tmp/disk2"),
1120
        (constants.FD_BLKTAP, "/tmp/disk3"),
1121
        (constants.FD_BLKTAP, "/tmp/disk4"),
1122
        ])
1123

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1234

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

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

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

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

    
1247

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

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

    
1256

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

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

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

    
1271

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

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

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

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

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

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

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

    
1295
    assert cur_progress == offset
1296

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

    
1300
    return (True, None)
1301

    
1302

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1399
    instance.WipeDisks(lu, inst)
1400

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

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

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

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

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

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

    
1441

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

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

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

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

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

    
1474

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

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

    
1485

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1601

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

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

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

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

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

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

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

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

    
1643

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1734

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2311

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

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

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

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

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

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

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

    
2350
    self.ExecOpCode(op)
2351

    
2352

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