Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / instance_unittest.py @ 07e68848

History | View | Annotate | Download (82.5 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2008, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Tests for LUInstance*
23

24
"""
25

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

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

    
42
from cmdlib.cmdlib_unittest import _StubComputeIPolicySpecViolation, _FakeLU
43

    
44
from testsupport import *
45

    
46
import testutils
47

    
48

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

    
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
445
  def testFileInstanceNoClusterStorage(self):
446
    self.cluster.file_storage_dir = None
447
    op = self.CopyOpCode(self.file_op)
448
    self.ExecOpCodeExpectOpPrereqError(
449
      op, "Cluster file storage dir 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
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1110
        (constants.FD_BLKTAP, "/tmp/disk2"),
1111
        (constants.FD_BLKTAP, "/tmp/disk3"),
1112
        (constants.FD_BLKTAP, "/tmp/disk4"),
1113
        ])
1114

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1225

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

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

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

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

    
1238

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

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

    
1247

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

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

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

    
1262

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

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

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

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

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

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

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

    
1286
    assert cur_progress == offset
1287

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

    
1291
    return (True, None)
1292

    
1293

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1390
    instance.WipeDisks(lu, inst)
1391

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

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

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

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

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

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

    
1432

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

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

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

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

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

    
1465

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

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

    
1476

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1570

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

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

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

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

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

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

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

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

    
1612

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1703

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1964
  def testHotAddNic(self):
1965
    op = self.CopyOpCode(self.op,
1966
                         nics=[(constants.DDM_ADD, -1, {})],
1967
                         hotplug=True)
1968
    self.ExecOpCode(op)
1969
    self.assertTrue(self.rpc.call_hotplug_supported.called)
1970
    self.assertTrue(self.rpc.call_hotplug_device.called)
1971

    
1972
  def testAddNicWithIp(self):
1973
    op = self.CopyOpCode(self.op,
1974
                         nics=[(constants.DDM_ADD, -1,
1975
                                {
1976
                                  constants.INIC_IP: "2.3.1.4"
1977
                                })])
1978
    self.ExecOpCode(op)
1979

    
1980
  def testModifyNicRoutedWithoutIp(self):
1981
    op = self.CopyOpCode(self.op,
1982
                         nics=[(constants.DDM_MODIFY, 0,
1983
                                {
1984
                                  constants.INIC_MODE: constants.NIC_MODE_ROUTED
1985
                                })])
1986
    self.ExecOpCodeExpectOpPrereqError(
1987
      op, "Cannot set the NIC IP address to None on a routed NIC")
1988

    
1989
  def testModifyNicSetMac(self):
1990
    op = self.CopyOpCode(self.op,
1991
                         nics=[(constants.DDM_MODIFY, 0,
1992
                                {
1993
                                  constants.INIC_MAC: "0a:12:95:15:bf:75"
1994
                                })])
1995
    self.ExecOpCode(op)
1996

    
1997
  def testModifyNicWithPoolIpNoNetwork(self):
1998
    op = self.CopyOpCode(self.op,
1999
                         nics=[(constants.DDM_MODIFY, -1,
2000
                                {
2001
                                  constants.INIC_IP: constants.NIC_IP_POOL
2002
                                })])
2003
    self.ExecOpCodeExpectOpPrereqError(
2004
      op, "ip=pool, but no network found")
2005

    
2006
  def testModifyNicSetNet(self):
2007
    old_net = self.cfg.AddNewNetwork()
2008
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2009
    inst = self.cfg.AddNewInstance(nics=[
2010
      self.cfg.CreateNic(network=old_net,
2011
                         ip="198.51.100.2")])
2012

    
2013
    new_net = self.cfg.AddNewNetwork(mac_prefix="be")
2014
    self.cfg.ConnectNetworkToGroup(new_net, self.group)
2015
    op = self.CopyOpCode(self.op,
2016
                         instance_name=inst.name,
2017
                         nics=[(constants.DDM_MODIFY, 0,
2018
                                {
2019
                                  constants.INIC_NETWORK: new_net.name
2020
                                })])
2021
    self.ExecOpCode(op)
2022

    
2023
  def testModifyNicSetLinkWhileConnected(self):
2024
    old_net = self.cfg.AddNewNetwork()
2025
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2026
    inst = self.cfg.AddNewInstance(nics=[
2027
      self.cfg.CreateNic(network=old_net)])
2028

    
2029
    op = self.CopyOpCode(self.op,
2030
                         instance_name=inst.name,
2031
                         nics=[(constants.DDM_MODIFY, 0,
2032
                                {
2033
                                  constants.INIC_LINK: "mock_link"
2034
                                })])
2035
    self.ExecOpCodeExpectOpPrereqError(
2036
      op, "Not allowed to change link or mode of a NIC that is connected"
2037
          " to a network")
2038

    
2039
  def testModifyNicSetNetAndIp(self):
2040
    net = self.cfg.AddNewNetwork(mac_prefix="be", network="123.123.123.0/24")
2041
    self.cfg.ConnectNetworkToGroup(net, self.group)
2042
    op = self.CopyOpCode(self.op,
2043
                         nics=[(constants.DDM_MODIFY, 0,
2044
                                {
2045
                                  constants.INIC_NETWORK: net.name,
2046
                                  constants.INIC_IP: "123.123.123.1"
2047
                                })])
2048
    self.ExecOpCode(op)
2049

    
2050
  def testModifyNic(self):
2051
    op = self.CopyOpCode(self.op,
2052
                         nics=[(constants.DDM_MODIFY, 0, {})])
2053
    self.ExecOpCode(op)
2054

    
2055
  def testHotModifyNic(self):
2056
    op = self.CopyOpCode(self.op,
2057
                         nics=[(constants.DDM_MODIFY, 0, {})],
2058
                         hotplug=True)
2059
    self.ExecOpCode(op)
2060
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2061
    self.assertTrue(self.rpc.call_hotplug_device.called)
2062

    
2063
  def testRemoveLastNic(self):
2064
    op = self.CopyOpCode(self.op,
2065
                         nics=[(constants.DDM_REMOVE, 0, {})])
2066
    self.ExecOpCodeExpectOpPrereqError(
2067
      op, "violates policy")
2068

    
2069
  def testRemoveNic(self):
2070
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2071
                                         self.cfg.CreateNic()])
2072
    op = self.CopyOpCode(self.op,
2073
                         instance_name=inst.name,
2074
                         nics=[(constants.DDM_REMOVE, 0, {})])
2075
    self.ExecOpCode(op)
2076

    
2077
  def testHotRemoveNic(self):
2078
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2079
                                         self.cfg.CreateNic()])
2080
    op = self.CopyOpCode(self.op,
2081
                         instance_name=inst.name,
2082
                         nics=[(constants.DDM_REMOVE, 0, {})],
2083
                         hotplug=True)
2084
    self.ExecOpCode(op)
2085
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2086
    self.assertTrue(self.rpc.call_hotplug_device.called)
2087

    
2088
  def testSetOffline(self):
2089
    op = self.CopyOpCode(self.op,
2090
                         offline=True)
2091
    self.ExecOpCode(op)
2092

    
2093
  def testUnsetOffline(self):
2094
    op = self.CopyOpCode(self.op,
2095
                         offline=False)
2096
    self.ExecOpCode(op)
2097

    
2098
  def testAddDiskInvalidMode(self):
2099
    op = self.CopyOpCode(self.op,
2100
                         disks=[[constants.DDM_ADD, -1,
2101
                                 {
2102
                                   constants.IDISK_MODE: "invalid"
2103
                                 }]])
2104
    self.ExecOpCodeExpectOpPrereqError(
2105
      op, "Invalid disk access mode 'invalid'")
2106

    
2107
  def testAddDiskMissingSize(self):
2108
    op = self.CopyOpCode(self.op,
2109
                         disks=[[constants.DDM_ADD, -1, {}]])
2110
    self.ExecOpCodeExpectOpPrereqError(
2111
      op, "Required disk parameter 'size' missing")
2112

    
2113
  def testAddDiskInvalidSize(self):
2114
    op = self.CopyOpCode(self.op,
2115
                         disks=[[constants.DDM_ADD, -1,
2116
                                 {
2117
                                   constants.IDISK_SIZE: "invalid"
2118
                                 }]])
2119
    self.ExecOpCodeExpectException(
2120
      op, errors.TypeEnforcementError, "is not a valid size")
2121

    
2122
  def testAddDiskRunningInstanceNoWaitForSync(self):
2123
    op = self.CopyOpCode(self.running_op,
2124
                         disks=[[constants.DDM_ADD, -1,
2125
                                 {
2126
                                   constants.IDISK_SIZE: 1024
2127
                                 }]],
2128
                         wait_for_sync=False)
2129
    self.ExecOpCodeExpectOpPrereqError(
2130
      op, "Can't add a disk to an instance with activated disks"
2131
          " and --no-wait-for-sync given.")
2132

    
2133
  def testAddDiskDownInstance(self):
2134
    op = self.CopyOpCode(self.op,
2135
                         disks=[[constants.DDM_ADD, -1,
2136
                                 {
2137
                                   constants.IDISK_SIZE: 1024
2138
                                 }]])
2139
    self.ExecOpCode(op)
2140

    
2141
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2142

    
2143
  def testAddDiskRunningInstance(self):
2144
    op = self.CopyOpCode(self.running_op,
2145
                         disks=[[constants.DDM_ADD, -1,
2146
                                 {
2147
                                   constants.IDISK_SIZE: 1024
2148
                                 }]])
2149
    self.ExecOpCode(op)
2150

    
2151
    self.assertFalse(self.rpc.call_blockdev_shutdown.called)
2152

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

    
2162
  def testHotAddDisk(self):
2163
    self.rpc.call_blockdev_assemble.return_value = \
2164
      self.RpcResultsBuilder() \
2165
        .CreateSuccessfulNodeResult(self.master, ("/dev/mocked_path",
2166
                                    "/var/run/ganeti/instance-disks/mocked_d"))
2167
    op = self.CopyOpCode(self.op,
2168
                         disks=[[constants.DDM_ADD, -1,
2169
                                 {
2170
                                   constants.IDISK_SIZE: 1024,
2171
                                 }]],
2172
                         hotplug=True)
2173
    self.ExecOpCode(op)
2174
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2175
    self.assertTrue(self.rpc.call_blockdev_create.called)
2176
    self.assertTrue(self.rpc.call_blockdev_assemble.called)
2177
    self.assertTrue(self.rpc.call_hotplug_device.called)
2178

    
2179
  def testHotRemoveDisk(self):
2180
    inst = self.cfg.AddNewInstance(disks=[self.cfg.CreateDisk(),
2181
                                          self.cfg.CreateDisk()])
2182
    op = self.CopyOpCode(self.op,
2183
                         instance_name=inst.name,
2184
                         disks=[[constants.DDM_REMOVE, -1,
2185
                                 {}]],
2186
                         hotplug=True)
2187
    self.ExecOpCode(op)
2188
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2189
    self.assertTrue(self.rpc.call_hotplug_device.called)
2190
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2191
    self.assertTrue(self.rpc.call_blockdev_remove.called)
2192

    
2193
  def testModifyDiskWithSize(self):
2194
    op = self.CopyOpCode(self.op,
2195
                         disks=[[constants.DDM_MODIFY, 0,
2196
                                 {
2197
                                   constants.IDISK_SIZE: 1024
2198
                                 }]])
2199
    self.ExecOpCodeExpectOpPrereqError(
2200
      op, "Disk size change not possible, use grow-disk")
2201

    
2202
  def testModifyDiskWithRandomParams(self):
2203
    op = self.CopyOpCode(self.op,
2204
                         disks=[[constants.DDM_MODIFY, 0,
2205
                                 {
2206
                                   constants.IDISK_METAVG: "new_meta_vg",
2207
                                   constants.IDISK_MODE: "invalid",
2208
                                   constants.IDISK_NAME: "new_name"
2209
                                 }]])
2210
    self.ExecOpCodeExpectOpPrereqError(
2211
      op, "Disk modification doesn't support additional arbitrary parameters")
2212

    
2213
  def testModifyDiskUnsetName(self):
2214
    op = self.CopyOpCode(self.op,
2215
                         disks=[[constants.DDM_MODIFY, 0,
2216
                                  {
2217
                                    constants.IDISK_NAME: constants.VALUE_NONE
2218
                                  }]])
2219
    self.ExecOpCode(op)
2220

    
2221
  def testSetOldDiskTemplate(self):
2222
    op = self.CopyOpCode(self.op,
2223
                         disk_template=self.inst.disk_template)
2224
    self.ExecOpCodeExpectOpPrereqError(
2225
      op, "Instance already has disk template")
2226

    
2227
  def testSetDisabledDiskTemplate(self):
2228
    self.cfg.SetEnabledDiskTemplates([self.inst.disk_template])
2229
    op = self.CopyOpCode(self.op,
2230
                         disk_template=constants.DT_EXT)
2231
    self.ExecOpCodeExpectOpPrereqError(
2232
      op, "Disk template .* is not enabled for this cluster")
2233

    
2234
  def testInvalidDiskTemplateConversion(self):
2235
    op = self.CopyOpCode(self.op,
2236
                         disk_template=constants.DT_EXT)
2237
    self.ExecOpCodeExpectOpPrereqError(
2238
      op, "Unsupported disk template conversion from .* to .*")
2239

    
2240
  def testConvertToDRBDWithSecondarySameAsPrimary(self):
2241
    op = self.CopyOpCode(self.op,
2242
                         disk_template=constants.DT_DRBD8,
2243
                         remote_node=self.master.name)
2244
    self.ExecOpCodeExpectOpPrereqError(
2245
      op, "Given new secondary node .* is the same as the primary node"
2246
          " of the instance")
2247

    
2248
  def testConvertPlainToDRBD(self):
2249
    self.rpc.call_blockdev_shutdown.return_value = \
2250
      self.RpcResultsBuilder() \
2251
        .CreateSuccessfulNodeResult(self.master, True)
2252
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2253
      self.RpcResultsBuilder() \
2254
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2255

    
2256
    op = self.CopyOpCode(self.op,
2257
                         disk_template=constants.DT_DRBD8,
2258
                         remote_node=self.snode.name)
2259
    self.ExecOpCode(op)
2260

    
2261
  def testConvertDRBDToPlain(self):
2262
    self.inst.disks = [self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
2263
                                           primary_node=self.master,
2264
                                           secondary_node=self.snode)]
2265
    self.inst.disk_template = constants.DT_DRBD8
2266
    self.rpc.call_blockdev_shutdown.return_value = \
2267
      self.RpcResultsBuilder() \
2268
        .CreateSuccessfulNodeResult(self.master, True)
2269
    self.rpc.call_blockdev_remove.return_value = \
2270
      self.RpcResultsBuilder() \
2271
        .CreateSuccessfulNodeResult(self.master)
2272
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2273
      self.RpcResultsBuilder() \
2274
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2275

    
2276
    op = self.CopyOpCode(self.op,
2277
                         disk_template=constants.DT_PLAIN)
2278
    self.ExecOpCode(op)
2279

    
2280

    
2281
class TestLUInstanceChangeGroup(CmdlibTestCase):
2282
  def setUp(self):
2283
    super(TestLUInstanceChangeGroup, self).setUp()
2284

    
2285
    self.group2 = self.cfg.AddNewNodeGroup()
2286
    self.node2 = self.cfg.AddNewNode(group=self.group2)
2287
    self.inst = self.cfg.AddNewInstance()
2288
    self.op = opcodes.OpInstanceChangeGroup(instance_name=self.inst.name)
2289

    
2290
  def testTargetGroupIsInstanceGroup(self):
2291
    op = self.CopyOpCode(self.op,
2292
                         target_groups=[self.group.name])
2293
    self.ExecOpCodeExpectOpPrereqError(
2294
      op, "Can't use group\(s\) .* as targets, they are used by the"
2295
          " instance .*")
2296

    
2297
  def testNoTargetGroups(self):
2298
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
2299
                                   primary_node=self.master,
2300
                                   secondary_node=self.node2)
2301
    op = self.CopyOpCode(self.op,
2302
                         instance_name=inst.name)
2303
    self.ExecOpCodeExpectOpPrereqError(
2304
      op, "There are no possible target groups")
2305

    
2306
  def testFailingIAllocator(self):
2307
    self.iallocator_cls.return_value.success = False
2308
    op = self.CopyOpCode(self.op)
2309

    
2310
    self.ExecOpCodeExpectOpPrereqError(
2311
      op, "Can't compute solution for changing group of instance .*"
2312
          " using iallocator .*")
2313

    
2314
  def testChangeGroup(self):
2315
    self.iallocator_cls.return_value.success = True
2316
    self.iallocator_cls.return_value.result = ([], [], [])
2317
    op = self.CopyOpCode(self.op)
2318

    
2319
    self.ExecOpCode(op)
2320

    
2321

    
2322
if __name__ == "__main__":
2323
  testutils.GanetiTestProgram()