Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (82.8 kB)

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

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

    
21

    
22
"""Tests for LUInstance*
23

24
"""
25

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

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

    
42
from cmdlib.cmdlib_unittest import _StubComputeIPolicySpecViolation, _FakeLU
43

    
44
from testsupport import *
45

    
46
import testutils
47

    
48

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

    
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
183
  def testVlanWithWrongMode(self):
184
    op = self.CopyOpCode(self.diskless_op,
185
                         nics=[{
186
                           constants.INIC_VLAN: ":1",
187
                           constants.INIC_MODE: constants.NIC_MODE_BRIDGED
188
                         }])
189
    self.ExecOpCodeExpectOpPrereqError(
190
      op, "VLAN is given, but network mode is not openvswitch")
191

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
749

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

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

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

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

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

    
774

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

    
779
    self.op = self.diskless_op
780

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

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

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

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

    
815

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
978

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

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

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

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

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

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

    
1001

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

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

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

    
1011
    self.lu = self.GetMockLU()
1012

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

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

    
1021
    assert disk_template not in constants.DISK_TEMPLATES
1022

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

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

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

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

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

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

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

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

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

    
1069
    return result
1070

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1234

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

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

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

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

    
1247

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

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

    
1256

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

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

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

    
1271

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

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

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

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

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

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

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

    
1295
    assert cur_progress == offset
1296

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

    
1300
    return (True, None)
1301

    
1302

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1399
    instance.WipeDisks(lu, inst)
1400

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

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

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

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

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

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

    
1441

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

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

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

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

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

    
1474

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

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

    
1485

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

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

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

    
1503
  def testMissingInstance(self):
1504
    op = opcodes.OpInstanceMove(instance_name="missing.inst",
1505
                                target_node=self.node.name)
1506
    self.ExecOpCodeExpectOpPrereqError(op, "Instance 'missing.inst' not known")
1507

    
1508
  def testUncopyableDiskTemplate(self):
1509
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_SHARED_FILE)
1510
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1511
                                target_node=self.node.name)
1512
    self.ExecOpCodeExpectOpPrereqError(
1513
      op, "Disk template sharedfile not suitable for copying")
1514

    
1515
  def testAlreadyOnTargetNode(self):
1516
    inst = self.cfg.AddNewInstance()
1517
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1518
                                target_node=self.master.name)
1519
    self.ExecOpCodeExpectOpPrereqError(
1520
      op, "Instance .* is already on the node .*")
1521

    
1522
  def testMoveStoppedInstance(self):
1523
    inst = self.cfg.AddNewInstance()
1524
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1525
                                target_node=self.node.name)
1526
    self.ExecOpCode(op)
1527

    
1528
  def testMoveRunningInstance(self):
1529
    self.rpc.call_node_info.return_value = \
1530
      self.RpcResultsBuilder() \
1531
        .AddSuccessfulNode(self.node,
1532
                           (NotImplemented, NotImplemented,
1533
                            ({"memory_free": 10000}, ))) \
1534
        .Build()
1535
    self.rpc.call_instance_start.return_value = \
1536
      self.RpcResultsBuilder() \
1537
        .CreateSuccessfulNodeResult(self.node, "")
1538

    
1539
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1540
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1541
                                target_node=self.node.name)
1542
    self.ExecOpCode(op)
1543

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

    
1555
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1556
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1557
                                target_node=self.node.name)
1558
    self.ExecOpCodeExpectOpExecError(
1559
      op, "Could not start instance .* on node .*")
1560

    
1561
  def testMoveFailingBlockdevAssemble(self):
1562
    inst = self.cfg.AddNewInstance()
1563
    self.rpc.call_blockdev_assemble.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
  def testMoveFailingBlockdevExport(self):
1571
    inst = self.cfg.AddNewInstance()
1572
    self.rpc.call_blockdev_export.return_value = \
1573
      self.RpcResultsBuilder() \
1574
        .CreateFailedNodeResult(self.node)
1575
    op = opcodes.OpInstanceMove(instance_name=inst.name,
1576
                                target_node=self.node.name)
1577
    self.ExecOpCodeExpectOpExecError(op, "Errors during disk copy")
1578

    
1579

    
1580
class TestLUInstanceRename(CmdlibTestCase):
1581
  def setUp(self):
1582
    super(TestLUInstanceRename, self).setUp()
1583

    
1584
    self.inst = self.cfg.AddNewInstance()
1585

    
1586
    self.op = opcodes.OpInstanceRename(instance_name=self.inst.name,
1587
                                       new_name="new_name.example.com")
1588

    
1589
  def testIpCheckWithoutNameCheck(self):
1590
    op = self.CopyOpCode(self.op,
1591
                         ip_check=True,
1592
                         name_check=False)
1593
    self.ExecOpCodeExpectOpPrereqError(
1594
      op, "IP address check requires a name check")
1595

    
1596
  def testIpAlreadyInUse(self):
1597
    self.netutils_mod.TcpPing.return_value = True
1598
    op = self.CopyOpCode(self.op)
1599
    self.ExecOpCodeExpectOpPrereqError(
1600
      op, "IP .* of instance .* already in use")
1601

    
1602
  def testExistingInstanceName(self):
1603
    self.cfg.AddNewInstance(name="new_name.example.com")
1604
    op = self.CopyOpCode(self.op)
1605
    self.ExecOpCodeExpectOpPrereqError(
1606
      op, "Instance .* is already in the cluster")
1607

    
1608
  def testFileInstance(self):
1609
    self.rpc.call_blockdev_assemble.return_value = \
1610
      self.RpcResultsBuilder() \
1611
        .CreateSuccessfulNodeResult(self.master, (None, None))
1612
    self.rpc.call_blockdev_shutdown.return_value = \
1613
      self.RpcResultsBuilder() \
1614
        .CreateSuccessfulNodeResult(self.master, None)
1615

    
1616
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_FILE)
1617
    op = self.CopyOpCode(self.op,
1618
                         instance_name=inst.name)
1619
    self.ExecOpCode(op)
1620

    
1621

    
1622
class TestLUInstanceMultiAlloc(CmdlibTestCase):
1623
  def setUp(self):
1624
    super(TestLUInstanceMultiAlloc, self).setUp()
1625

    
1626
    self.inst_op = opcodes.OpInstanceCreate(instance_name="inst.example.com",
1627
                                            disk_template=constants.DT_DRBD8,
1628
                                            disks=[],
1629
                                            nics=[],
1630
                                            os_type="mock_os",
1631
                                            hypervisor=constants.HT_XEN_HVM,
1632
                                            mode=constants.INSTANCE_CREATE)
1633

    
1634
  def testInstanceWithIAllocator(self):
1635
    inst = self.CopyOpCode(self.inst_op,
1636
                           iallocator="mock")
1637
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1638
    self.ExecOpCodeExpectOpPrereqError(
1639
      op, "iallocator are not allowed to be set on instance objects")
1640

    
1641
  def testOnlySomeNodesGiven(self):
1642
    inst1 = self.CopyOpCode(self.inst_op,
1643
                            pnode=self.master.name)
1644
    inst2 = self.CopyOpCode(self.inst_op)
1645
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1646
    self.ExecOpCodeExpectOpPrereqError(
1647
      op, "There are instance objects providing pnode/snode while others"
1648
          " do not")
1649

    
1650
  def testMissingIAllocator(self):
1651
    self.cluster.default_iallocator = None
1652
    inst = self.CopyOpCode(self.inst_op)
1653
    op = opcodes.OpInstanceMultiAlloc(instances=[inst])
1654
    self.ExecOpCodeExpectOpPrereqError(
1655
      op, "No iallocator or nodes on the instances given and no cluster-wide"
1656
          " default iallocator found")
1657

    
1658
  def testDuplicateInstanceNames(self):
1659
    inst1 = self.CopyOpCode(self.inst_op)
1660
    inst2 = self.CopyOpCode(self.inst_op)
1661
    op = opcodes.OpInstanceMultiAlloc(instances=[inst1, inst2])
1662
    self.ExecOpCodeExpectOpPrereqError(
1663
      op, "There are duplicate instance names")
1664

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

    
1673
  def testDryRun(self):
1674
    snode = self.cfg.AddNewNode()
1675
    inst = self.CopyOpCode(self.inst_op,
1676
                           pnode=self.master.name,
1677
                           snode=snode.name)
1678
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1679
                                      dry_run=True)
1680
    self.ExecOpCode(op)
1681

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

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

    
1692
  def testWithIAllocatorOpportunisticLocking(self):
1693
    snode = self.cfg.AddNewNode()
1694
    self.iallocator_cls.return_value.result = \
1695
      ([("inst.example.com", [self.master.name, snode.name])], [])
1696

    
1697
    inst = self.CopyOpCode(self.inst_op)
1698
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1699
                                      iallocator="mock_ialloc",
1700
                                      opportunistic_locking=True)
1701
    self.ExecOpCode(op)
1702

    
1703
  def testFailingIAllocator(self):
1704
    self.iallocator_cls.return_value.success = False
1705

    
1706
    inst = self.CopyOpCode(self.inst_op)
1707
    op = opcodes.OpInstanceMultiAlloc(instances=[inst],
1708
                                      iallocator="mock_ialloc")
1709
    self.ExecOpCodeExpectOpPrereqError(
1710
      op, "Can't compute nodes using iallocator")
1711

    
1712

    
1713
class TestLUInstanceSetParams(CmdlibTestCase):
1714
  def setUp(self):
1715
    super(TestLUInstanceSetParams, self).setUp()
1716

    
1717
    self.inst = self.cfg.AddNewInstance()
1718
    self.op = opcodes.OpInstanceSetParams(instance_name=self.inst.name)
1719

    
1720
    self.running_inst = \
1721
      self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP)
1722
    self.running_op = \
1723
      opcodes.OpInstanceSetParams(instance_name=self.running_inst.name)
1724

    
1725
    self.snode = self.cfg.AddNewNode()
1726

    
1727
    self.mocked_storage_type = constants.ST_LVM_VG
1728
    self.mocked_storage_free = 10000
1729
    self.mocked_master_cpu_total = 16
1730
    self.mocked_master_memory_free = 2048
1731
    self.mocked_snode_cpu_total = 16
1732
    self.mocked_snode_memory_free = 512
1733

    
1734
    self.mocked_running_inst_memory = 1024
1735
    self.mocked_running_inst_vcpus = 8
1736
    self.mocked_running_inst_state = "running"
1737
    self.mocked_running_inst_time = 10938474
1738

    
1739
    bootid = "mock_bootid"
1740
    storage_info = [
1741
      {
1742
        "type": self.mocked_storage_type,
1743
        "storage_free": self.mocked_storage_free
1744
      }
1745
    ]
1746
    hv_info_master = {
1747
      "cpu_total": self.mocked_master_cpu_total,
1748
      "memory_free": self.mocked_master_memory_free
1749
    }
1750
    hv_info_snode = {
1751
      "cpu_total": self.mocked_snode_cpu_total,
1752
      "memory_free": self.mocked_snode_memory_free
1753
    }
1754

    
1755
    self.rpc.call_node_info.return_value = \
1756
      self.RpcResultsBuilder() \
1757
        .AddSuccessfulNode(self.master,
1758
                           (bootid, storage_info, (hv_info_master, ))) \
1759
        .AddSuccessfulNode(self.snode,
1760
                           (bootid, storage_info, (hv_info_snode, ))) \
1761
        .Build()
1762

    
1763
    def _InstanceInfo(_, instance, __, ___):
1764
      if instance == self.inst.name:
1765
        return self.RpcResultsBuilder() \
1766
          .CreateSuccessfulNodeResult(self.master, None)
1767
      elif instance == self.running_inst.name:
1768
        return self.RpcResultsBuilder() \
1769
          .CreateSuccessfulNodeResult(
1770
            self.master, {
1771
              "memory": self.mocked_running_inst_memory,
1772
              "vcpus": self.mocked_running_inst_vcpus,
1773
              "state": self.mocked_running_inst_state,
1774
              "time": self.mocked_running_inst_time
1775
            })
1776
      else:
1777
        raise AssertionError()
1778
    self.rpc.call_instance_info.side_effect = _InstanceInfo
1779

    
1780
    self.rpc.call_bridges_exist.return_value = \
1781
      self.RpcResultsBuilder() \
1782
        .CreateSuccessfulNodeResult(self.master, True)
1783

    
1784
    self.rpc.call_blockdev_getmirrorstatus.side_effect = \
1785
      lambda node, _: self.RpcResultsBuilder() \
1786
                        .CreateSuccessfulNodeResult(node, [])
1787

    
1788
    self.rpc.call_blockdev_shutdown.side_effect = \
1789
      lambda node, _: self.RpcResultsBuilder() \
1790
                        .CreateSuccessfulNodeResult(node, [])
1791

    
1792
  def testNoChanges(self):
1793
    op = self.CopyOpCode(self.op)
1794
    self.ExecOpCodeExpectOpPrereqError(op, "No changes submitted")
1795

    
1796
  def testGlobalHvparams(self):
1797
    op = self.CopyOpCode(self.op,
1798
                         hvparams={constants.HV_MIGRATION_PORT: 1234})
1799
    self.ExecOpCodeExpectOpPrereqError(
1800
      op, "hypervisor parameters are global and cannot be customized")
1801

    
1802
  def testHvparams(self):
1803
    op = self.CopyOpCode(self.op,
1804
                         hvparams={constants.HV_BOOT_ORDER: "cd"})
1805
    self.ExecOpCode(op)
1806

    
1807
  def testDisksAndDiskTemplate(self):
1808
    op = self.CopyOpCode(self.op,
1809
                         disk_template=constants.DT_PLAIN,
1810
                         disks=[[constants.DDM_ADD, -1, {}]])
1811
    self.ExecOpCodeExpectOpPrereqError(
1812
      op, "Disk template conversion and other disk changes not supported at"
1813
          " the same time")
1814

    
1815
  def testDiskTemplateToMirroredNoRemoteNode(self):
1816
    op = self.CopyOpCode(self.op,
1817
                         disk_template=constants.DT_DRBD8)
1818
    self.ExecOpCodeExpectOpPrereqError(
1819
      op, "Changing the disk template to a mirrored one requires specifying"
1820
          " a secondary node")
1821

    
1822
  def testPrimaryNodeToOldPrimaryNode(self):
1823
    op = self.CopyOpCode(self.op,
1824
                         pnode=self.master.name)
1825
    self.ExecOpCode(op)
1826

    
1827
  def testPrimaryNodeChange(self):
1828
    node = self.cfg.AddNewNode()
1829
    op = self.CopyOpCode(self.op,
1830
                         pnode=node.name)
1831
    self.ExecOpCode(op)
1832

    
1833
  def testPrimaryNodeChangeRunningInstance(self):
1834
    node = self.cfg.AddNewNode()
1835
    op = self.CopyOpCode(self.running_op,
1836
                         pnode=node.name)
1837
    self.ExecOpCodeExpectOpPrereqError(op, "Instance is still running")
1838

    
1839
  def testOsChange(self):
1840
    os = self.cfg.CreateOs(supported_variants=[])
1841
    self.rpc.call_os_get.return_value = \
1842
      self.RpcResultsBuilder() \
1843
        .CreateSuccessfulNodeResult(self.master, os)
1844
    op = self.CopyOpCode(self.op,
1845
                         os_name=os.name)
1846
    self.ExecOpCode(op)
1847

    
1848
  def testVCpuChange(self):
1849
    op = self.CopyOpCode(self.op,
1850
                         beparams={
1851
                           constants.BE_VCPUS: 4
1852
                         })
1853
    self.ExecOpCode(op)
1854

    
1855
  def testWrongCpuMask(self):
1856
    op = self.CopyOpCode(self.op,
1857
                         beparams={
1858
                           constants.BE_VCPUS: 4
1859
                         },
1860
                         hvparams={
1861
                           constants.HV_CPU_MASK: "1,2:3,4"
1862
                         })
1863
    self.ExecOpCodeExpectOpPrereqError(
1864
      op, "Number of vCPUs .* does not match the CPU mask .*")
1865

    
1866
  def testCorrectCpuMask(self):
1867
    op = self.CopyOpCode(self.op,
1868
                         beparams={
1869
                           constants.BE_VCPUS: 4
1870
                         },
1871
                         hvparams={
1872
                           constants.HV_CPU_MASK: "1,2:3,4:all:1,4"
1873
                         })
1874
    self.ExecOpCode(op)
1875

    
1876
  def testOsParams(self):
1877
    op = self.CopyOpCode(self.op,
1878
                         osparams={
1879
                           self.os.supported_parameters[0]: "test_param_val"
1880
                         })
1881
    self.ExecOpCode(op)
1882

    
1883
  def testIncreaseMemoryTooMuch(self):
1884
    op = self.CopyOpCode(self.running_op,
1885
                         beparams={
1886
                           constants.BE_MAXMEM:
1887
                             self.mocked_master_memory_free * 2
1888
                         })
1889
    self.ExecOpCodeExpectOpPrereqError(
1890
      op, "This change will prevent the instance from starting")
1891

    
1892
  def testIncreaseMemory(self):
1893
    op = self.CopyOpCode(self.running_op,
1894
                         beparams={
1895
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1896
                         })
1897
    self.ExecOpCode(op)
1898

    
1899
  def testIncreaseMemoryTooMuchForSecondary(self):
1900
    inst = self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP,
1901
                                   disk_template=constants.DT_DRBD8,
1902
                                   secondary_node=self.snode)
1903
    self.rpc.call_instance_info.side_effect = [
1904
      self.RpcResultsBuilder()
1905
        .CreateSuccessfulNodeResult(self.master,
1906
                                    {
1907
                                      "memory":
1908
                                        self.mocked_snode_memory_free * 2,
1909
                                      "vcpus": self.mocked_running_inst_vcpus,
1910
                                      "state": self.mocked_running_inst_state,
1911
                                      "time": self.mocked_running_inst_time
1912
                                    })]
1913

    
1914
    op = self.CopyOpCode(self.op,
1915
                         instance_name=inst.name,
1916
                         beparams={
1917
                           constants.BE_MAXMEM:
1918
                             self.mocked_snode_memory_free * 2,
1919
                           constants.BE_AUTO_BALANCE: True
1920
                         })
1921
    self.ExecOpCodeExpectOpPrereqError(
1922
      op, "This change will prevent the instance from failover to its"
1923
          " secondary node")
1924

    
1925
  def testInvalidRuntimeMemory(self):
1926
    op = self.CopyOpCode(self.running_op,
1927
                         runtime_mem=self.mocked_master_memory_free * 2)
1928
    self.ExecOpCodeExpectOpPrereqError(
1929
      op, "Instance .* must have memory between .* and .* of memory")
1930

    
1931
  def testIncreaseRuntimeMemory(self):
1932
    op = self.CopyOpCode(self.running_op,
1933
                         runtime_mem=self.mocked_master_memory_free,
1934
                         beparams={
1935
                           constants.BE_MAXMEM: self.mocked_master_memory_free
1936
                         })
1937
    self.ExecOpCode(op)
1938

    
1939
  def testAddNicWithPoolIpNoNetwork(self):
1940
    op = self.CopyOpCode(self.op,
1941
                         nics=[(constants.DDM_ADD, -1,
1942
                                {
1943
                                  constants.INIC_IP: constants.NIC_IP_POOL
1944
                                })])
1945
    self.ExecOpCodeExpectOpPrereqError(
1946
      op, "If ip=pool, parameter network cannot be none")
1947

    
1948
  def testAddNicWithPoolIp(self):
1949
    net = self.cfg.AddNewNetwork()
1950
    self.cfg.ConnectNetworkToGroup(net, self.group)
1951
    op = self.CopyOpCode(self.op,
1952
                         nics=[(constants.DDM_ADD, -1,
1953
                                {
1954
                                  constants.INIC_IP: constants.NIC_IP_POOL,
1955
                                  constants.INIC_NETWORK: net.name
1956
                                })])
1957
    self.ExecOpCode(op)
1958

    
1959
  def testAddNicWithInvalidIp(self):
1960
    op = self.CopyOpCode(self.op,
1961
                         nics=[(constants.DDM_ADD, -1,
1962
                                {
1963
                                  constants.INIC_IP: "invalid"
1964
                                })])
1965
    self.ExecOpCodeExpectOpPrereqError(
1966
      op, "Invalid IP address")
1967

    
1968
  def testAddNic(self):
1969
    op = self.CopyOpCode(self.op,
1970
                         nics=[(constants.DDM_ADD, -1, {})])
1971
    self.ExecOpCode(op)
1972

    
1973
  def testHotAddNic(self):
1974
    op = self.CopyOpCode(self.op,
1975
                         nics=[(constants.DDM_ADD, -1, {})],
1976
                         hotplug=True)
1977
    self.ExecOpCode(op)
1978
    self.assertTrue(self.rpc.call_hotplug_supported.called)
1979
    self.assertTrue(self.rpc.call_hotplug_device.called)
1980

    
1981
  def testAddNicWithIp(self):
1982
    op = self.CopyOpCode(self.op,
1983
                         nics=[(constants.DDM_ADD, -1,
1984
                                {
1985
                                  constants.INIC_IP: "2.3.1.4"
1986
                                })])
1987
    self.ExecOpCode(op)
1988

    
1989
  def testModifyNicRoutedWithoutIp(self):
1990
    op = self.CopyOpCode(self.op,
1991
                         nics=[(constants.DDM_MODIFY, 0,
1992
                                {
1993
                                  constants.INIC_MODE: constants.NIC_MODE_ROUTED
1994
                                })])
1995
    self.ExecOpCodeExpectOpPrereqError(
1996
      op, "Cannot set the NIC IP address to None on a routed NIC")
1997

    
1998
  def testModifyNicSetMac(self):
1999
    op = self.CopyOpCode(self.op,
2000
                         nics=[(constants.DDM_MODIFY, 0,
2001
                                {
2002
                                  constants.INIC_MAC: "0a:12:95:15:bf:75"
2003
                                })])
2004
    self.ExecOpCode(op)
2005

    
2006
  def testModifyNicWithPoolIpNoNetwork(self):
2007
    op = self.CopyOpCode(self.op,
2008
                         nics=[(constants.DDM_MODIFY, -1,
2009
                                {
2010
                                  constants.INIC_IP: constants.NIC_IP_POOL
2011
                                })])
2012
    self.ExecOpCodeExpectOpPrereqError(
2013
      op, "ip=pool, but no network found")
2014

    
2015
  def testModifyNicSetNet(self):
2016
    old_net = self.cfg.AddNewNetwork()
2017
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2018
    inst = self.cfg.AddNewInstance(nics=[
2019
      self.cfg.CreateNic(network=old_net,
2020
                         ip="198.51.100.2")])
2021

    
2022
    new_net = self.cfg.AddNewNetwork(mac_prefix="be")
2023
    self.cfg.ConnectNetworkToGroup(new_net, self.group)
2024
    op = self.CopyOpCode(self.op,
2025
                         instance_name=inst.name,
2026
                         nics=[(constants.DDM_MODIFY, 0,
2027
                                {
2028
                                  constants.INIC_NETWORK: new_net.name
2029
                                })])
2030
    self.ExecOpCode(op)
2031

    
2032
  def testModifyNicSetLinkWhileConnected(self):
2033
    old_net = self.cfg.AddNewNetwork()
2034
    self.cfg.ConnectNetworkToGroup(old_net, self.group)
2035
    inst = self.cfg.AddNewInstance(nics=[
2036
      self.cfg.CreateNic(network=old_net)])
2037

    
2038
    op = self.CopyOpCode(self.op,
2039
                         instance_name=inst.name,
2040
                         nics=[(constants.DDM_MODIFY, 0,
2041
                                {
2042
                                  constants.INIC_LINK: "mock_link"
2043
                                })])
2044
    self.ExecOpCodeExpectOpPrereqError(
2045
      op, "Not allowed to change link or mode of a NIC that is connected"
2046
          " to a network")
2047

    
2048
  def testModifyNicSetNetAndIp(self):
2049
    net = self.cfg.AddNewNetwork(mac_prefix="be", network="123.123.123.0/24")
2050
    self.cfg.ConnectNetworkToGroup(net, self.group)
2051
    op = self.CopyOpCode(self.op,
2052
                         nics=[(constants.DDM_MODIFY, 0,
2053
                                {
2054
                                  constants.INIC_NETWORK: net.name,
2055
                                  constants.INIC_IP: "123.123.123.1"
2056
                                })])
2057
    self.ExecOpCode(op)
2058

    
2059
  def testModifyNic(self):
2060
    op = self.CopyOpCode(self.op,
2061
                         nics=[(constants.DDM_MODIFY, 0, {})])
2062
    self.ExecOpCode(op)
2063

    
2064
  def testHotModifyNic(self):
2065
    op = self.CopyOpCode(self.op,
2066
                         nics=[(constants.DDM_MODIFY, 0, {})],
2067
                         hotplug=True)
2068
    self.ExecOpCode(op)
2069
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2070
    self.assertTrue(self.rpc.call_hotplug_device.called)
2071

    
2072
  def testRemoveLastNic(self):
2073
    op = self.CopyOpCode(self.op,
2074
                         nics=[(constants.DDM_REMOVE, 0, {})])
2075
    self.ExecOpCodeExpectOpPrereqError(
2076
      op, "violates policy")
2077

    
2078
  def testRemoveNic(self):
2079
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2080
                                         self.cfg.CreateNic()])
2081
    op = self.CopyOpCode(self.op,
2082
                         instance_name=inst.name,
2083
                         nics=[(constants.DDM_REMOVE, 0, {})])
2084
    self.ExecOpCode(op)
2085

    
2086
  def testHotRemoveNic(self):
2087
    inst = self.cfg.AddNewInstance(nics=[self.cfg.CreateNic(),
2088
                                         self.cfg.CreateNic()])
2089
    op = self.CopyOpCode(self.op,
2090
                         instance_name=inst.name,
2091
                         nics=[(constants.DDM_REMOVE, 0, {})],
2092
                         hotplug=True)
2093
    self.ExecOpCode(op)
2094
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2095
    self.assertTrue(self.rpc.call_hotplug_device.called)
2096

    
2097
  def testSetOffline(self):
2098
    op = self.CopyOpCode(self.op,
2099
                         offline=True)
2100
    self.ExecOpCode(op)
2101

    
2102
  def testUnsetOffline(self):
2103
    op = self.CopyOpCode(self.op,
2104
                         offline=False)
2105
    self.ExecOpCode(op)
2106

    
2107
  def testAddDiskInvalidMode(self):
2108
    op = self.CopyOpCode(self.op,
2109
                         disks=[[constants.DDM_ADD, -1,
2110
                                 {
2111
                                   constants.IDISK_MODE: "invalid"
2112
                                 }]])
2113
    self.ExecOpCodeExpectOpPrereqError(
2114
      op, "Invalid disk access mode 'invalid'")
2115

    
2116
  def testAddDiskMissingSize(self):
2117
    op = self.CopyOpCode(self.op,
2118
                         disks=[[constants.DDM_ADD, -1, {}]])
2119
    self.ExecOpCodeExpectOpPrereqError(
2120
      op, "Required disk parameter 'size' missing")
2121

    
2122
  def testAddDiskInvalidSize(self):
2123
    op = self.CopyOpCode(self.op,
2124
                         disks=[[constants.DDM_ADD, -1,
2125
                                 {
2126
                                   constants.IDISK_SIZE: "invalid"
2127
                                 }]])
2128
    self.ExecOpCodeExpectException(
2129
      op, errors.TypeEnforcementError, "is not a valid size")
2130

    
2131
  def testAddDiskRunningInstanceNoWaitForSync(self):
2132
    op = self.CopyOpCode(self.running_op,
2133
                         disks=[[constants.DDM_ADD, -1,
2134
                                 {
2135
                                   constants.IDISK_SIZE: 1024
2136
                                 }]],
2137
                         wait_for_sync=False)
2138
    self.ExecOpCodeExpectOpPrereqError(
2139
      op, "Can't add a disk to an instance with activated disks"
2140
          " and --no-wait-for-sync given.")
2141

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

    
2150
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2151

    
2152
  def testAddDiskRunningInstance(self):
2153
    op = self.CopyOpCode(self.running_op,
2154
                         disks=[[constants.DDM_ADD, -1,
2155
                                 {
2156
                                   constants.IDISK_SIZE: 1024
2157
                                 }]])
2158
    self.ExecOpCode(op)
2159

    
2160
    self.assertFalse(self.rpc.call_blockdev_shutdown.called)
2161

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

    
2171
  def testHotAddDisk(self):
2172
    self.rpc.call_blockdev_assemble.return_value = \
2173
      self.RpcResultsBuilder() \
2174
        .CreateSuccessfulNodeResult(self.master, ("/dev/mocked_path",
2175
                                    "/var/run/ganeti/instance-disks/mocked_d"))
2176
    op = self.CopyOpCode(self.op,
2177
                         disks=[[constants.DDM_ADD, -1,
2178
                                 {
2179
                                   constants.IDISK_SIZE: 1024,
2180
                                 }]],
2181
                         hotplug=True)
2182
    self.ExecOpCode(op)
2183
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2184
    self.assertTrue(self.rpc.call_blockdev_create.called)
2185
    self.assertTrue(self.rpc.call_blockdev_assemble.called)
2186
    self.assertTrue(self.rpc.call_hotplug_device.called)
2187

    
2188
  def testHotRemoveDisk(self):
2189
    inst = self.cfg.AddNewInstance(disks=[self.cfg.CreateDisk(),
2190
                                          self.cfg.CreateDisk()])
2191
    op = self.CopyOpCode(self.op,
2192
                         instance_name=inst.name,
2193
                         disks=[[constants.DDM_REMOVE, -1,
2194
                                 {}]],
2195
                         hotplug=True)
2196
    self.ExecOpCode(op)
2197
    self.assertTrue(self.rpc.call_hotplug_supported.called)
2198
    self.assertTrue(self.rpc.call_hotplug_device.called)
2199
    self.assertTrue(self.rpc.call_blockdev_shutdown.called)
2200
    self.assertTrue(self.rpc.call_blockdev_remove.called)
2201

    
2202
  def testModifyDiskWithSize(self):
2203
    op = self.CopyOpCode(self.op,
2204
                         disks=[[constants.DDM_MODIFY, 0,
2205
                                 {
2206
                                   constants.IDISK_SIZE: 1024
2207
                                 }]])
2208
    self.ExecOpCodeExpectOpPrereqError(
2209
      op, "Disk size change not possible, use grow-disk")
2210

    
2211
  def testModifyDiskWithRandomParams(self):
2212
    op = self.CopyOpCode(self.op,
2213
                         disks=[[constants.DDM_MODIFY, 0,
2214
                                 {
2215
                                   constants.IDISK_METAVG: "new_meta_vg",
2216
                                   constants.IDISK_MODE: "invalid",
2217
                                   constants.IDISK_NAME: "new_name"
2218
                                 }]])
2219
    self.ExecOpCodeExpectOpPrereqError(
2220
      op, "Disk modification doesn't support additional arbitrary parameters")
2221

    
2222
  def testModifyDiskUnsetName(self):
2223
    op = self.CopyOpCode(self.op,
2224
                         disks=[[constants.DDM_MODIFY, 0,
2225
                                  {
2226
                                    constants.IDISK_NAME: constants.VALUE_NONE
2227
                                  }]])
2228
    self.ExecOpCode(op)
2229

    
2230
  def testSetOldDiskTemplate(self):
2231
    op = self.CopyOpCode(self.op,
2232
                         disk_template=self.inst.disk_template)
2233
    self.ExecOpCodeExpectOpPrereqError(
2234
      op, "Instance already has disk template")
2235

    
2236
  def testSetDisabledDiskTemplate(self):
2237
    self.cfg.SetEnabledDiskTemplates([self.inst.disk_template])
2238
    op = self.CopyOpCode(self.op,
2239
                         disk_template=constants.DT_EXT)
2240
    self.ExecOpCodeExpectOpPrereqError(
2241
      op, "Disk template .* is not enabled for this cluster")
2242

    
2243
  def testInvalidDiskTemplateConversion(self):
2244
    op = self.CopyOpCode(self.op,
2245
                         disk_template=constants.DT_EXT)
2246
    self.ExecOpCodeExpectOpPrereqError(
2247
      op, "Unsupported disk template conversion from .* to .*")
2248

    
2249
  def testConvertToDRBDWithSecondarySameAsPrimary(self):
2250
    op = self.CopyOpCode(self.op,
2251
                         disk_template=constants.DT_DRBD8,
2252
                         remote_node=self.master.name)
2253
    self.ExecOpCodeExpectOpPrereqError(
2254
      op, "Given new secondary node .* is the same as the primary node"
2255
          " of the instance")
2256

    
2257
  def testConvertPlainToDRBD(self):
2258
    self.rpc.call_blockdev_shutdown.return_value = \
2259
      self.RpcResultsBuilder() \
2260
        .CreateSuccessfulNodeResult(self.master, True)
2261
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2262
      self.RpcResultsBuilder() \
2263
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2264

    
2265
    op = self.CopyOpCode(self.op,
2266
                         disk_template=constants.DT_DRBD8,
2267
                         remote_node=self.snode.name)
2268
    self.ExecOpCode(op)
2269

    
2270
  def testConvertDRBDToPlain(self):
2271
    self.inst.disks = [self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
2272
                                           primary_node=self.master,
2273
                                           secondary_node=self.snode)]
2274
    self.inst.disk_template = constants.DT_DRBD8
2275
    self.rpc.call_blockdev_shutdown.return_value = \
2276
      self.RpcResultsBuilder() \
2277
        .CreateSuccessfulNodeResult(self.master, True)
2278
    self.rpc.call_blockdev_remove.return_value = \
2279
      self.RpcResultsBuilder() \
2280
        .CreateSuccessfulNodeResult(self.master)
2281
    self.rpc.call_blockdev_getmirrorstatus.return_value = \
2282
      self.RpcResultsBuilder() \
2283
        .CreateSuccessfulNodeResult(self.master, [objects.BlockDevStatus()])
2284

    
2285
    op = self.CopyOpCode(self.op,
2286
                         disk_template=constants.DT_PLAIN)
2287
    self.ExecOpCode(op)
2288

    
2289

    
2290
class TestLUInstanceChangeGroup(CmdlibTestCase):
2291
  def setUp(self):
2292
    super(TestLUInstanceChangeGroup, self).setUp()
2293

    
2294
    self.group2 = self.cfg.AddNewNodeGroup()
2295
    self.node2 = self.cfg.AddNewNode(group=self.group2)
2296
    self.inst = self.cfg.AddNewInstance()
2297
    self.op = opcodes.OpInstanceChangeGroup(instance_name=self.inst.name)
2298

    
2299
  def testTargetGroupIsInstanceGroup(self):
2300
    op = self.CopyOpCode(self.op,
2301
                         target_groups=[self.group.name])
2302
    self.ExecOpCodeExpectOpPrereqError(
2303
      op, "Can't use group\(s\) .* as targets, they are used by the"
2304
          " instance .*")
2305

    
2306
  def testNoTargetGroups(self):
2307
    inst = self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
2308
                                   primary_node=self.master,
2309
                                   secondary_node=self.node2)
2310
    op = self.CopyOpCode(self.op,
2311
                         instance_name=inst.name)
2312
    self.ExecOpCodeExpectOpPrereqError(
2313
      op, "There are no possible target groups")
2314

    
2315
  def testFailingIAllocator(self):
2316
    self.iallocator_cls.return_value.success = False
2317
    op = self.CopyOpCode(self.op)
2318

    
2319
    self.ExecOpCodeExpectOpPrereqError(
2320
      op, "Can't compute solution for changing group of instance .*"
2321
          " using iallocator .*")
2322

    
2323
  def testChangeGroup(self):
2324
    self.iallocator_cls.return_value.success = True
2325
    self.iallocator_cls.return_value.result = ([], [], [])
2326
    op = self.CopyOpCode(self.op)
2327

    
2328
    self.ExecOpCode(op)
2329

    
2330

    
2331
if __name__ == "__main__":
2332
  testutils.GanetiTestProgram()