Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / instance_unittest.py @ 876cbf2a

History | View | Annotate | Download (84 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 re
29
import unittest
30
import mock
31
import operator
32

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

    
43
from cmdlib.cmdlib_unittest import _StubComputeIPolicySpecViolation, _FakeLU
44

    
45
from testsupport import *
46

    
47
import testutils
48

    
49

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

    
67

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
216
  def testRoutedNoIp(self):
217
    op = self.CopyOpCode(self.diskless_op,
218
                         nics=[{
219
                           constants.INIC_MODE: constants.NIC_MODE_ROUTED
220
                         }])
221
    self.ExecOpCode(op)
222

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
740

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

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

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

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

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

    
765

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

    
770
    self.op = self.diskless_op
771

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

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

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

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

    
806

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
969

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

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

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

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

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

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

    
992

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

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

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

    
1002
    self.lu = self.GetMockLU()
1003

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

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

    
1012
    assert disk_template not in constants.DISK_TEMPLATES
1013

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

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

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

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

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

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

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

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

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

    
1060
    return result
1061

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

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

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

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

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

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

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

    
1109
      for (idx, disk) in enumerate(result):
1110
        (file_driver, file_storage_dir) = disk.logical_id
1111
        dir_fmt = r"^/tmp/.*\.%s\.disk%d$" % (disk_template, idx + 2)
1112
        self.assertEqual(file_driver, constants.FD_BLKTAP)
1113
        # FIXME: use assertIsNotNone when py 2.7 is minimum supported version
1114
        self.assertNotEqual(re.match(dir_fmt, file_storage_dir), None)
1115

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1226

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

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

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

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

    
1239

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

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

    
1248

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

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

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

    
1263

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

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

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

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

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

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

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

    
1287
    assert cur_progress == offset
1288

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

    
1292
    return (True, None)
1293

    
1294

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1391
    instance.WipeDisks(lu, inst)
1392

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

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

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

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

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

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

    
1433

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

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

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

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

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

    
1466

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

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

    
1477

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

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

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

    
1495
  def testMissingInstance(self):
1496
    op = opcodes.OpInstanceMove(instance_name="missing.inst",
1497
                                target_node=self.node.name)
1498
    self.ExecOpCodeExpectOpPrereqError(op, "Instance 'missing.inst' not known")
1499

    
1500
  def testUncopyableDiskTemplate(self):
1501
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_SHARED_FILE)
1502
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1503
                                target_node=self.node.name)
1504
    self.ExecOpCodeExpectOpPrereqError(
1505
      op, "Disk template sharedfile not suitable for copying")
1506

    
1507
  def testAlreadyOnTargetNode(self):
1508
    inst = self.cfg.AddNewInstance()
1509
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1510
                                target_node=self.master.name)
1511
    self.ExecOpCodeExpectOpPrereqError(
1512
      op, "Instance .* is already on the node .*")
1513

    
1514
  def testMoveStoppedInstance(self):
1515
    inst = self.cfg.AddNewInstance()
1516
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1517
                                target_node=self.node.name)
1518
    self.ExecOpCode(op)
1519

    
1520
  def testMoveRunningInstance(self):
1521
    self.rpc.call_node_info.return_value = \
1522
      self.RpcResultsBuilder() \
1523
        .AddSuccessfulNode(self.node,
1524
                           (NotImplemented, NotImplemented,
1525
                            ({"memory_free": 10000}, ))) \
1526
        .Build()
1527
    self.rpc.call_instance_start.return_value = \
1528
      self.RpcResultsBuilder() \
1529
        .CreateSuccessfulNodeResult(self.node, "")
1530

    
1531
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1532
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1533
                                target_node=self.node.name)
1534
    self.ExecOpCode(op)
1535

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

    
1547
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1548
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1549
                                target_node=self.node.name)
1550
    self.ExecOpCodeExpectOpExecError(
1551
      op, "Could not start instance .* on node .*")
1552

    
1553
  def testMoveFailingBlockdevAssemble(self):
1554
    inst = self.cfg.AddNewInstance()
1555
    self.rpc.call_blockdev_assemble.return_value = \
1556
      self.RpcResultsBuilder() \
1557
        .CreateFailedNodeResult(self.node)
1558
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1559
                                target_node=self.node.name)
1560
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1561

    
1562
  def testMoveFailingBlockdevExport(self):
1563
    inst = self.cfg.AddNewInstance()
1564
    self.rpc.call_blockdev_export.return_value = \
1565
      self.RpcResultsBuilder() \
1566
        .CreateFailedNodeResult(self.node)
1567
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1568
                                target_node=self.node.name)
1569
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1570

    
1571

    
1572
class TestLUInstanceRename(CmdlibTestCase):
1573
  def setUp(self):
1574
    super(TestLUInstanceRename, self).setUp()
1575

    
1576
    self.inst = self.cfg.AddNewInstance()
1577

    
1578
    self.op = opcodes.OpInstanceRename(instance_name=self.inst.name,
1579
                                       new_name="new_name.example.com")
1580

    
1581
  def testIpCheckWithoutNameCheck(self):
1582
    op = self.CopyOpCode(self.op,
1583
                         ip_check=True,
1584
                         name_check=False)
1585
    self.ExecOpCodeExpectOpPrereqError(
1586
      op, "IP address check requires a name check")
1587

    
1588
  def testIpAlreadyInUse(self):
1589
    self.netutils_mod.TcpPing.return_value = True
1590
    op = self.CopyOpCode(self.op)
1591
    self.ExecOpCodeExpectOpPrereqError(
1592
      op, "IP .* of instance .* already in use")
1593

    
1594
  def testExistingInstanceName(self):
1595
    self.cfg.AddNewInstance(name="new_name.example.com")
1596
    op = self.CopyOpCode(self.op)
1597
    self.ExecOpCodeExpectOpPrereqError(
1598
      op, "Instance .* is already in the cluster")
1599

    
1600
  def testFileInstance(self):
1601
    self.rpc.call_blockdev_assemble.return_value = \
1602
      self.RpcResultsBuilder() \
1603
        .CreateSuccessfulNodeResult(self.master, (None, None))
1604
    self.rpc.call_blockdev_shutdown.return_value = \
1605
      self.RpcResultsBuilder() \
1606
        .CreateSuccessfulNodeResult(self.master, None)
1607

    
1608
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_FILE)
1609
    op = self.CopyOpCode(self.op,
1610
                         instance_name=inst.name)
1611
    self.ExecOpCode(op)
1612

    
1613

    
1614
class TestLUInstanceMultiAlloc(CmdlibTestCase):
1615
  def setUp(self):
1616
    super(TestLUInstanceMultiAlloc, self).setUp()
1617

    
1618
    self.inst_op = opcodes.OpInstanceCreate(instance_name="inst.example.com",
1619
                                            disk_template=constants.DT_DRBD8,
1620
                                            disks=[],
1621
                                            nics=[],
1622
                                            os_type="mock_os",
1623
                                            hypervisor=constants.HT_XEN_HVM,
1624
                                            mode=constants.INSTANCE_CREATE)
1625

    
1626
  def testInstanceWithIAllocator(self):
1627
    inst = self.CopyOpCode(self.inst_op,
1628
                           iallocator="mock")
1629
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1630
    self.ExecOpCodeExpectOpPrereqError(
1631
      op, "iallocator are not allowed to be set on instance objects")
1632

    
1633
  def testOnlySomeNodesGiven(self):
1634
    inst1 = self.CopyOpCode(self.inst_op,
1635
                            pnode=self.master.name)
1636
    inst2 = self.CopyOpCode(self.inst_op)
1637
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1638
    self.ExecOpCodeExpectOpPrereqError(
1639
      op, "There are instance objects providing pnode/snode while others"
1640
          " do not")
1641

    
1642
  def testMissingIAllocator(self):
1643
    self.cluster.default_iallocator = None
1644
    inst = self.CopyOpCode(self.inst_op)
1645
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1646
    self.ExecOpCodeExpectOpPrereqError(
1647
      op, "No iallocator or nodes on the instances given and no cluster-wide"
1648
          " default iallocator found")
1649

    
1650
  def testDuplicateInstanceNames(self):
1651
    inst1 = self.CopyOpCode(self.inst_op)
1652
    inst2 = self.CopyOpCode(self.inst_op)
1653
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1654
    self.ExecOpCodeExpectOpPrereqError(
1655
      op, "There are duplicate instance names")
1656

    
1657
  def testWithGivenNodes(self):
1658
    snode = self.cfg.AddNewNode()
1659
    inst = self.CopyOpCode(self.inst_op,
1660
                           pnode=self.master.name,
1661
                           snode=snode.name)
1662
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1663
    self.ExecOpCode(op)
1664

    
1665
  def testDryRun(self):
1666
    snode = self.cfg.AddNewNode()
1667
    inst = self.CopyOpCode(self.inst_op,
1668
                           pnode=self.master.name,
1669
                           snode=snode.name)
1670
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1671
                                      dry_run=True)
1672
    self.ExecOpCode(op)
1673

    
1674
  def testWithIAllocator(self):
1675
    snode = self.cfg.AddNewNode()
1676
    self.iallocator_cls.return_value.result = \
1677
      ([("inst.example.com", [self.master.name, snode.name])], [])
1678

    
1679
    inst = self.CopyOpCode(self.inst_op)
1680
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1681
                                      iallocator="mock_ialloc")
1682
    self.ExecOpCode(op)
1683

    
1684
  def testWithIAllocatorOpportunisticLocking(self):
1685
    snode = self.cfg.AddNewNode()
1686
    self.iallocator_cls.return_value.result = \
1687
      ([("inst.example.com", [self.master.name, snode.name])], [])
1688

    
1689
    inst = self.CopyOpCode(self.inst_op)
1690
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1691
                                      iallocator="mock_ialloc",
1692
                                      opportunistic_locking=True)
1693
    self.ExecOpCode(op)
1694

    
1695
  def testFailingIAllocator(self):
1696
    self.iallocator_cls.return_value.success = False
1697

    
1698
    inst = self.CopyOpCode(self.inst_op)
1699
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1700
                                      iallocator="mock_ialloc")
1701
    self.ExecOpCodeExpectOpPrereqError(
1702
      op, "Can't compute nodes using iallocator")
1703

    
1704

    
1705
class TestLUInstanceSetParams(CmdlibTestCase):
1706
  def setUp(self):
1707
    super(TestLUInstanceSetParams, self).setUp()
1708

    
1709
    self.inst = self.cfg.AddNewInstance()
1710
    self.op = opcodes.OpInstanceSetParams(instance_name=self.inst.name)
1711

    
1712
    self.running_inst = \
1713
      self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1714
    self.running_op = \
1715
      opcodes.OpInstanceSetParams(instance_name=self.running_inst.name)
1716

    
1717
    self.snode = self.cfg.AddNewNode()
1718

    
1719
    self.mocked_storage_type = constants.ST_LVM_VG
1720
    self.mocked_storage_free = 10000
1721
    self.mocked_master_cpu_total = 16
1722
    self.mocked_master_memory_free = 2048
1723
    self.mocked_snode_cpu_total = 16
1724
    self.mocked_snode_memory_free = 512
1725

    
1726
    self.mocked_running_inst_memory = 1024
1727
    self.mocked_running_inst_vcpus = 8
1728
    self.mocked_running_inst_state = "running"
1729
    self.mocked_running_inst_time = 10938474
1730

    
1731
    bootid = "mock_bootid"
1732
    storage_info = [
1733
      {
1734
        "type": self.mocked_storage_type,
1735
        "storage_free": self.mocked_storage_free
1736
      }
1737
    ]
1738
    hv_info_master = {
1739
      "cpu_total": self.mocked_master_cpu_total,
1740
      "memory_free": self.mocked_master_memory_free
1741
    }
1742
    hv_info_snode = {
1743
      "cpu_total": self.mocked_snode_cpu_total,
1744
      "memory_free": self.mocked_snode_memory_free
1745
    }
1746

    
1747
    self.rpc.call_node_info.return_value = \
1748
      self.RpcResultsBuilder() \
1749
        .AddSuccessfulNode(self.master,
1750
                           (bootid, storage_info, (hv_info_master, ))) \
1751
        .AddSuccessfulNode(self.snode,
1752
                           (bootid, storage_info, (hv_info_snode, ))) \
1753
        .Build()
1754

    
1755
    def _InstanceInfo(_, instance, __, ___):
1756
      if instance == self.inst.name:
1757
        return self.RpcResultsBuilder() \
1758
          .CreateSuccessfulNodeResult(self.master, None)
1759
      elif instance == self.running_inst.name:
1760
        return self.RpcResultsBuilder() \
1761
          .CreateSuccessfulNodeResult(
1762
            self.master, {
1763
              "memory": self.mocked_running_inst_memory,
1764
              "vcpus": self.mocked_running_inst_vcpus,
1765
              "state": self.mocked_running_inst_state,
1766
              "time": self.mocked_running_inst_time
1767
            })
1768
      else:
1769
        raise AssertionError()
1770
    self.rpc.call_instance_info.side_effect = _InstanceInfo
1771

    
1772
    self.rpc.call_bridges_exist.return_value = \
1773
      self.RpcResultsBuilder() \
1774
        .CreateSuccessfulNodeResult(self.master, True)
1775

    
1776
    self.rpc.call_blockdev_getmirrorstatus.side_effect = \
1777
      lambda node, _: self.RpcResultsBuilder() \
1778
                        .CreateSuccessfulNodeResult(node, [])
1779

    
1780
    self.rpc.call_blockdev_shutdown.side_effect = \
1781
      lambda node, _: self.RpcResultsBuilder() \
1782
                        .CreateSuccessfulNodeResult(node, [])
1783

    
1784
  def testNoChanges(self):
1785
    op = self.CopyOpCode(self.op)
1786
    self.ExecOpCodeExpectOpPrereqError(op, "No changes submitted")
1787

    
1788
  def testGlobalHvparams(self):
1789
    op = self.CopyOpCode(self.op,
1790
                         hvparams={constants.HV_MIGRATION_PORT: 1234})
1791
    self.ExecOpCodeExpectOpPrereqError(
1792
      op, "hypervisor parameters are global and cannot be customized")
1793

    
1794
  def testHvparams(self):
1795
    op = self.CopyOpCode(self.op,
1796
                         hvparams={constants.HV_BOOT_ORDER: "cd"})
1797
    self.ExecOpCode(op)
1798

    
1799
  def testDisksAndDiskTemplate(self):
1800
    op = self.CopyOpCode(self.op,
1801
                         disk_template=constants.DT_PLAIN,
1802
                         disks=[[constants.DDM_ADD, -1, {}]])
1803
    self.ExecOpCodeExpectOpPrereqError(
1804
      op, "Disk template conversion and other disk changes not supported at"
1805
          " the same time")
1806

    
1807
  def testDiskTemplateToMirroredNoRemoteNode(self):
1808
    op = self.CopyOpCode(self.op,
1809
                         disk_template=constants.DT_DRBD8)
1810
    self.ExecOpCodeExpectOpPrereqError(
1811
      op, "Changing the disk template to a mirrored one requires specifying"
1812
          " a secondary node")
1813

    
1814
  def testPrimaryNodeToOldPrimaryNode(self):
1815
    op = self.CopyOpCode(self.op,
1816
                         pnode=self.master.name)
1817
    self.ExecOpCode(op)
1818

    
1819
  def testPrimaryNodeChange(self):
1820
    node = self.cfg.AddNewNode()
1821
    op = self.CopyOpCode(self.op,
1822
                         pnode=node.name)
1823
    self.ExecOpCode(op)
1824

    
1825
  def testPrimaryNodeChangeRunningInstance(self):
1826
    node = self.cfg.AddNewNode()
1827
    op = self.CopyOpCode(self.running_op,
1828
                         pnode=node.name)
1829
    self.ExecOpCodeExpectOpPrereqError(op, "Instance is still running")
1830

    
1831
  def testOsChange(self):
1832
    os = self.cfg.CreateOs(supported_variants=[])
1833
    self.rpc.call_os_get.return_value = \
1834
      self.RpcResultsBuilder() \
1835
        .CreateSuccessfulNodeResult(self.master, os)
1836
    op = self.CopyOpCode(self.op,
1837
                         os_name=os.name)
1838
    self.ExecOpCode(op)
1839

    
1840
  def testVCpuChange(self):
1841
    op = self.CopyOpCode(self.op,
1842
                         beparams={
1843
                           constants.BE_VCPUS: 4
1844
                         })
1845
    self.ExecOpCode(op)
1846

    
1847
  def testWrongCpuMask(self):
1848
    op = self.CopyOpCode(self.op,
1849
                         beparams={
1850
                           constants.BE_VCPUS: 4
1851
                         },
1852
                         hvparams={
1853
                           constants.HV_CPU_MASK: "1,2:3,4"
1854
                         })
1855
    self.ExecOpCodeExpectOpPrereqError(
1856
      op, "Number of vCPUs .* does not match the CPU mask .*")
1857

    
1858
  def testCorrectCpuMask(self):
1859
    op = self.CopyOpCode(self.op,
1860
                         beparams={
1861
                           constants.BE_VCPUS: 4
1862
                         },
1863
                         hvparams={
1864
                           constants.HV_CPU_MASK: "1,2:3,4:all:1,4"
1865
                         })
1866
    self.ExecOpCode(op)
1867

    
1868
  def testOsParams(self):
1869
    op = self.CopyOpCode(self.op,
1870
                         osparams={
1871
                           self.os.supported_parameters[0]: "test_param_val"
1872
                         })
1873
    self.ExecOpCode(op)
1874

    
1875
  def testIncreaseMemoryTooMuch(self):
1876
    op = self.CopyOpCode(self.running_op,
1877
                         beparams={
1878
                           constants.BE_MAXMEM:
1879
                             self.mocked_master_memory_free * 2
1880
                         })
1881
    self.ExecOpCodeExpectOpPrereqError(
1882
      op, "This change will prevent the instance from starting")
1883

    
1884
  def testIncreaseMemory(self):
1885
    op = self.CopyOpCode(self.running_op,
1886
                         beparams={
1887
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1888
                         })
1889
    self.ExecOpCode(op)
1890

    
1891
  def testIncreaseMemoryTooMuchForSecondary(self):
1892
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP,
1893
                                   disk_template=constants.DT_DRBD8,
1894
                                   secondary_node=self.snode)
1895
    self.rpc.call_instance_info.side_effect = [
1896
      self.RpcResultsBuilder()
1897
        .CreateSuccessfulNodeResult(self.master,
1898
                                    {
1899
                                      "memory":
1900
                                        self.mocked_snode_memory_free * 2,
1901
                                      "vcpus": self.mocked_running_inst_vcpus,
1902
                                      "state": self.mocked_running_inst_state,
1903
                                      "time": self.mocked_running_inst_time
1904
                                    })]
1905

    
1906
    op = self.CopyOpCode(self.op,
1907
                         instance_name=inst.name,
1908
                         beparams={
1909
                           constants.BE_MAXMEM:
1910
                             self.mocked_snode_memory_free * 2,
1911
                           constants.BE_AUTO_BALANCE: True
1912
                         })
1913
    self.ExecOpCodeExpectOpPrereqError(
1914
      op, "This change will prevent the instance from failover to its"
1915
          " secondary node")
1916

    
1917
  def testInvalidRuntimeMemory(self):
1918
    op = self.CopyOpCode(self.running_op,
1919
                         runtime_mem=self.mocked_master_memory_free * 2)
1920
    self.ExecOpCodeExpectOpPrereqError(
1921
      op, "Instance .* must have memory between .* and .* of memory")
1922

    
1923
  def testIncreaseRuntimeMemory(self):
1924
    op = self.CopyOpCode(self.running_op,
1925
                         runtime_mem=self.mocked_master_memory_free,
1926
                         beparams={
1927
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1928
                         })
1929
    self.ExecOpCode(op)
1930

    
1931
  def testAddNicWithPoolIpNoNetwork(self):
1932
    op = self.CopyOpCode(self.op,
1933
                         nics=[(constants.DDM_ADD, -1,
1934
                                {
1935
                                  constants.INIC_IP: constants.NIC_IP_POOL
1936
                                })])
1937
    self.ExecOpCodeExpectOpPrereqError(
1938
      op, "If ip=pool, parameter network cannot be none")
1939

    
1940
  def testAddNicWithPoolIp(self):
1941
    net = self.cfg.AddNewNetwork()
1942
    self.cfg.ConnectNetworkToGroup(net, self.group)
1943
    op = self.CopyOpCode(self.op,
1944
                         nics=[(constants.DDM_ADD, -1,
1945
                                {
1946
                                  constants.INIC_IP: constants.NIC_IP_POOL,
1947
                                  constants.INIC_NETWORK: net.name
1948
                                })])
1949
    self.ExecOpCode(op)
1950

    
1951
  def testAddNicWithInvalidIp(self):
1952
    op = self.CopyOpCode(self.op,
1953
                         nics=[(constants.DDM_ADD, -1,
1954
                                {
1955
                                  constants.INIC_IP: "invalid"
1956
                                })])
1957
    self.ExecOpCodeExpectOpPrereqError(
1958
      op, "Invalid IP address")
1959

    
1960
  def testAddNic(self):
1961
    op = self.CopyOpCode(self.op,
1962
                         nics=[(constants.DDM_ADD, -1, {})])
1963
    self.ExecOpCode(op)
1964

    
1965
  def testNoHotplugSupport(self):
1966
    op = self.CopyOpCode(self.op,
1967
                         nics=[(constants.DDM_ADD, -1, {})],
1968
                         hotplug=True)
1969
    self.rpc.call_hotplug_supported.return_value = \
1970
      self.RpcResultsBuilder() \
1971
        .CreateFailedNodeResult(self.master)
1972
    self.ExecOpCodeExpectOpPrereqError(op, "Hotplug is not possible")
1973
    self.assertTrue(self.rpc.call_hotplug_supported.called)
1974

    
1975
  def testHotplugIfPossible(self):
1976
    op = self.CopyOpCode(self.op,
1977
                         nics=[(constants.DDM_ADD, -1, {})],
1978
                         hotplug_if_possible=True)
1979
    self.rpc.call_hotplug_supported.return_value = \
1980
      self.RpcResultsBuilder() \
1981
        .CreateFailedNodeResult(self.master)
1982
    self.ExecOpCode(op)
1983
    self.assertTrue(self.rpc.call_hotplug_supported.called)
1984
    self.assertFalse(self.rpc.call_hotplug_device.called)
1985

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

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

    
2005
  def testModifyNicRoutedWithoutIp(self):
2006
    op = self.CopyOpCode(self.op,
2007
                         nics=[(constants.DDM_MODIFY, 0,
2008
                                {
2009
                                  constants.INIC_MODE: constants.NIC_MODE_ROUTED
2010
                                })])
2011
    self.ExecOpCode(op)
2012

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

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

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

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

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

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

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

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

    
2079
  def testHotModifyNic(self):
2080
    op = self.CopyOpCode(self.op,
2081
                         nics=[(constants.DDM_MODIFY, 0, {})],
2082
                         hotplug=True)
2083
    self.rpc.call_hotplug_supported.return_value = \
2084
      self.RpcResultsBuilder() \
2085
        .CreateSuccessfulNodeResult(self.master)
2086
    self.ExecOpCode(op)
2087
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2088
    self.assertTrue(self.rpc.call_hotplug_device.called)
2089

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2229
  def testModifyDiskWithSize(self):
2230
    op = self.CopyOpCode(self.op,
2231
                         disks=[[constants.DDM_MODIFY, 0,
2232
                                 {
2233
                                   constants.IDISK_SIZE: 1024
2234
                                 }]])
2235
    self.ExecOpCodeExpectOpPrereqError(
2236
      op, "Disk size change not possible, use grow-disk")
2237

    
2238
  def testModifyDiskWithRandomParams(self):
2239
    op = self.CopyOpCode(self.op,
2240
                         disks=[[constants.DDM_MODIFY, 0,
2241
                                 {
2242
                                   constants.IDISK_METAVG: "new_meta_vg",
2243
                                   constants.IDISK_MODE: "invalid",
2244
                                   constants.IDISK_NAME: "new_name"
2245
                                 }]])
2246
    self.ExecOpCodeExpectException(op, errors.TypeEnforcementError,
2247
                                   "Unknown parameter 'metavg'")
2248

    
2249
  def testModifyDiskUnsetName(self):
2250
    op = self.CopyOpCode(self.op,
2251
                         disks=[[constants.DDM_MODIFY, 0,
2252
                                  {
2253
                                    constants.IDISK_NAME: constants.VALUE_NONE
2254
                                  }]])
2255
    self.ExecOpCode(op)
2256

    
2257
  def testSetOldDiskTemplate(self):
2258
    op = self.CopyOpCode(self.op,
2259
                         disk_template=self.inst.disk_template)
2260
    self.ExecOpCodeExpectOpPrereqError(
2261
      op, "Instance already has disk template")
2262

    
2263
  def testSetDisabledDiskTemplate(self):
2264
    self.cfg.SetEnabledDiskTemplates([self.inst.disk_template])
2265
    op = self.CopyOpCode(self.op,
2266
                         disk_template=constants.DT_EXT)
2267
    self.ExecOpCodeExpectOpPrereqError(
2268
      op, "Disk template .* is not enabled for this cluster")
2269

    
2270
  def testInvalidDiskTemplateConversion(self):
2271
    op = self.CopyOpCode(self.op,
2272
                         disk_template=constants.DT_EXT)
2273
    self.ExecOpCodeExpectOpPrereqError(
2274
      op, "Unsupported disk template conversion from .* to .*")
2275

    
2276
  def testConvertToDRBDWithSecondarySameAsPrimary(self):
2277
    op = self.CopyOpCode(self.op,
2278
                         disk_template=constants.DT_DRBD8,
2279
                         remote_node=self.master.name)
2280
    self.ExecOpCodeExpectOpPrereqError(
2281
      op, "Given new secondary node .* is the same as the primary node"
2282
          " of the instance")
2283

    
2284
  def testConvertPlainToDRBD(self):
2285
    self.rpc.call_blockdev_shutdown.return_value = \
2286
      self.RpcResultsBuilder() \
2287
        .CreateSuccessfulNodeResult(self.master, True)
2288
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2289
      self.RpcResultsBuilder() \
2290
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2291

    
2292
    op = self.CopyOpCode(self.op,
2293
                         disk_template=constants.DT_DRBD8,
2294
                         remote_node=self.snode.name)
2295
    self.ExecOpCode(op)
2296

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

    
2312
    op = self.CopyOpCode(self.op,
2313
                         disk_template=constants.DT_PLAIN)
2314
    self.ExecOpCode(op)
2315

    
2316

    
2317
class TestLUInstanceChangeGroup(CmdlibTestCase):
2318
  def setUp(self):
2319
    super(TestLUInstanceChangeGroup, self).setUp()
2320

    
2321
    self.group2 = self.cfg.AddNewNodeGroup()
2322
    self.node2 = self.cfg.AddNewNode(group=self.group2)
2323
    self.inst = self.cfg.AddNewInstance()
2324
    self.op = opcodes.OpInstanceChangeGroup(instance_name=self.inst.name)
2325

    
2326
  def testTargetGroupIsInstanceGroup(self):
2327
    op = self.CopyOpCode(self.op,
2328
                         target_groups=[self.group.name])
2329
    self.ExecOpCodeExpectOpPrereqError(
2330
      op, "Can't use group\(s\) .* as targets, they are used by the"
2331
          " instance .*")
2332

    
2333
  def testNoTargetGroups(self):
2334
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
2335
                                   primary_node=self.master,
2336
                                   secondary_node=self.node2)
2337
    op = self.CopyOpCode(self.op,
2338
                         instance_name=inst.name)
2339
    self.ExecOpCodeExpectOpPrereqError(
2340
      op, "There are no possible target groups")
2341

    
2342
  def testFailingIAllocator(self):
2343
    self.iallocator_cls.return_value.success = False
2344
    op = self.CopyOpCode(self.op)
2345

    
2346
    self.ExecOpCodeExpectOpPrereqError(
2347
      op, "Can't compute solution for changing group of instance .*"
2348
          " using iallocator .*")
2349

    
2350
  def testChangeGroup(self):
2351
    self.iallocator_cls.return_value.success = True
2352
    self.iallocator_cls.return_value.result = ([], [], [])
2353
    op = self.CopyOpCode(self.op)
2354

    
2355
    self.ExecOpCode(op)
2356

    
2357

    
2358
if __name__ == "__main__":
2359
  testutils.GanetiTestProgram()