Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.rapi.rlib2_unittest.py @ 319b7012

History | View | Annotate | Download (46.8 kB)

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

    
4
# Copyright (C) 2010, 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
"""Script for unittesting the RAPI rlib2 module
23

24
"""
25

    
26

    
27
import unittest
28
import itertools
29
import random
30

    
31
from ganeti import constants
32
from ganeti import opcodes
33
from ganeti import compat
34
from ganeti import http
35
from ganeti import query
36
import ganeti.rpc.errors as rpcerr
37
from ganeti import errors
38
from ganeti import rapi
39

    
40
from ganeti.rapi import rlib2
41
from ganeti.rapi import baserlib
42
from ganeti.rapi import connector
43

    
44
import testutils
45

    
46

    
47
class _FakeRequestPrivateData:
48
  def __init__(self, body_data):
49
    self.body_data = body_data
50

    
51

    
52
class _FakeRequest:
53
  def __init__(self, body_data):
54
    self.private = _FakeRequestPrivateData(body_data)
55

    
56

    
57
def _CreateHandler(cls, items, queryargs, body_data, client_cls):
58
  return cls(items, queryargs, _FakeRequest(body_data),
59
             _client_cls=client_cls)
60

    
61

    
62
class _FakeClient:
63
  def __init__(self, address=None):
64
    self._jobs = []
65

    
66
  def GetNextSubmittedJob(self):
67
    return self._jobs.pop(0)
68

    
69
  def SubmitJob(self, ops):
70
    job_id = str(1 + int(random.random() * 1000000))
71
    self._jobs.append((job_id, ops))
72
    return job_id
73

    
74

    
75
class _FakeClientFactory:
76
  def __init__(self, cls):
77
    self._client_cls = cls
78
    self._clients = []
79

    
80
  def GetNextClient(self):
81
    return self._clients.pop(0)
82

    
83
  def __call__(self, address=None):
84
    cl = self._client_cls(address=address)
85
    self._clients.append(cl)
86
    return cl
87

    
88

    
89
class RAPITestCase(testutils.GanetiTestCase):
90
  """Provides a few helper methods specific to RAPI testing.
91

92
  """
93
  def __init__(self, *args, **kwargs):
94
    """Creates a fake client factory the test may or may not use.
95

96
    """
97
    unittest.TestCase.__init__(self, *args, **kwargs)
98
    self._clfactory = _FakeClientFactory(_FakeClient)
99

    
100
  def assertNoNextClient(self, clfactory=None):
101
    """Insures that no further clients are present.
102

103
    """
104
    if clfactory is None:
105
      clfactory = self._clfactory
106

    
107
    self.assertRaises(IndexError, clfactory.GetNextClient)
108

    
109
  def getSubmittedOpcode(self, rapi_cls, items, query_args, body_data,
110
                         method_name, opcode_cls):
111
    """Submits a RAPI request and fetches the resulting opcode.
112

113
    """
114
    handler = _CreateHandler(rapi_cls, items, query_args, body_data,
115
                             self._clfactory)
116
    self.assertTrue(hasattr(handler, method_name),
117
                    "Handler lacks target method %s" % method_name)
118
    job_id = getattr(handler, method_name)()
119
    try:
120
      int(job_id)
121
    except ValueError:
122
      raise AssertionError("Returned value not job id; received %s" % job_id)
123

    
124
    cl = self._clfactory.GetNextClient()
125
    self.assertNoNextClient()
126

    
127
    (exp_job_id, (op, )) = cl.GetNextSubmittedJob()
128
    self.assertEqual(job_id, exp_job_id,
129
                     "Job IDs do not match: %s != %s" % (job_id, exp_job_id))
130
    self.assertRaises(IndexError, cl.GetNextSubmittedJob)
131

    
132
    self.assertTrue(isinstance(op, opcode_cls),
133
                    "Wrong opcode class: expected %s, got %s" %
134
                    (opcode_cls.__name__, op.__class__.__name__))
135

    
136
    return op
137

    
138

    
139
class TestConstants(unittest.TestCase):
140
  def testConsole(self):
141
    # Exporting the console field without authentication might expose
142
    # information
143
    assert "console" in query.INSTANCE_FIELDS
144
    self.assertTrue("console" not in rlib2.I_FIELDS)
145

    
146
  def testFields(self):
147
    checks = {
148
      constants.QR_INSTANCE: rlib2.I_FIELDS,
149
      constants.QR_NODE: rlib2.N_FIELDS,
150
      constants.QR_GROUP: rlib2.G_FIELDS,
151
      }
152

    
153
    for (qr, fields) in checks.items():
154
      self.assertFalse(set(fields) - set(query.ALL_FIELDS[qr].keys()))
155

    
156

    
157
class TestClientConnectError(unittest.TestCase):
158
  @staticmethod
159
  def _FailingClient(address=None):
160
    raise rpcerr.NoMasterError("test")
161

    
162
  def test(self):
163
    resources = [
164
      rlib2.R_2_groups,
165
      rlib2.R_2_instances,
166
      rlib2.R_2_nodes,
167
      ]
168
    for cls in resources:
169
      handler = _CreateHandler(cls, ["name"], {}, None, self._FailingClient)
170
      self.assertRaises(http.HttpBadGateway, handler.GET)
171

    
172

    
173
class TestJobSubmitError(unittest.TestCase):
174
  class _SubmitErrorClient:
175
    def __init__(self, address=None):
176
      pass
177

    
178
    @staticmethod
179
    def SubmitJob(ops):
180
      raise errors.JobQueueFull("test")
181

    
182
  def test(self):
183
    handler = _CreateHandler(rlib2.R_2_redist_config, [], {}, None,
184
                             self._SubmitErrorClient)
185
    self.assertRaises(http.HttpServiceUnavailable, handler.PUT)
186

    
187

    
188
class TestClusterModify(RAPITestCase):
189
  def test(self):
190
    body_data = {
191
      "vg_name": "testvg",
192
      "candidate_pool_size": 100,
193
      }
194
    op = self.getSubmittedOpcode(rlib2.R_2_cluster_modify, [], {}, body_data,
195
                                 "PUT", opcodes.OpClusterSetParams)
196

    
197
    self.assertEqual(op.vg_name, "testvg")
198
    self.assertEqual(op.candidate_pool_size, 100)
199

    
200
  def testInvalidValue(self):
201
    for attr in ["vg_name", "candidate_pool_size", "beparams", "_-Unknown#"]:
202
      handler = _CreateHandler(rlib2.R_2_cluster_modify, [], {}, {
203
        attr: True,
204
        }, self._clfactory)
205
      self.assertRaises(http.HttpBadRequest, handler.PUT)
206
      self.assertNoNextClient()
207

    
208

    
209
class TestRedistConfig(RAPITestCase):
210
  def test(self):
211
    self.getSubmittedOpcode(rlib2.R_2_redist_config, [], {}, None, "PUT",
212
                            opcodes.OpClusterRedistConf)
213

    
214

    
215
class TestNodeMigrate(RAPITestCase):
216
  def test(self):
217
    body_data = {
218
      "iallocator": "fooalloc",
219
      }
220
    op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_migrate, ["node1"], {},
221
                                 body_data, "POST", opcodes.OpNodeMigrate)
222
    self.assertEqual(op.node_name, "node1")
223
    self.assertEqual(op.iallocator, "fooalloc")
224

    
225
  def testQueryArgsConflict(self):
226
    query_args = {
227
      "live": True,
228
      "mode": constants.HT_MIGRATION_NONLIVE,
229
      }
230
    handler = _CreateHandler(rlib2.R_2_nodes_name_migrate, ["node2"],
231
                             query_args, None, self._clfactory)
232
    self.assertRaises(http.HttpBadRequest, handler.POST)
233
    self.assertNoNextClient()
234

    
235
  def testQueryArgsMode(self):
236
    query_args = {
237
      "mode": [constants.HT_MIGRATION_LIVE],
238
      }
239
    op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_migrate, ["node17292"],
240
                                 query_args, None, "POST",
241
                                 opcodes.OpNodeMigrate)
242
    self.assertEqual(op.node_name, "node17292")
243
    self.assertEqual(op.mode, constants.HT_MIGRATION_LIVE)
244

    
245
  def testQueryArgsLive(self):
246
    clfactory = _FakeClientFactory(_FakeClient)
247

    
248
    for live in [False, True]:
249
      query_args = {
250
        "live": [str(int(live))],
251
        }
252
      op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_migrate, ["node6940"],
253
                                   query_args, None, "POST",
254
                                   opcodes.OpNodeMigrate)
255

    
256
      self.assertEqual(op.node_name, "node6940")
257
      if live:
258
        self.assertEqual(op.mode, constants.HT_MIGRATION_LIVE)
259
      else:
260
        self.assertEqual(op.mode, constants.HT_MIGRATION_NONLIVE)
261

    
262

    
263
class TestNodeEvacuate(RAPITestCase):
264
  def test(self):
265
    query_args = {
266
      "dry-run": ["1"],
267
      }
268
    body_data = {
269
      "mode": constants.NODE_EVAC_SEC,
270
      }
271
    op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_evacuate, ["node92"],
272
                                 query_args, body_data, "POST",
273
                                 opcodes.OpNodeEvacuate)
274
    self.assertEqual(op.node_name, "node92")
275
    self.assertEqual(op.mode, constants.NODE_EVAC_SEC)
276

    
277

    
278
class TestNodePowercycle(RAPITestCase):
279
  def test(self):
280
    query_args = {
281
      "force": ["1"],
282
      }
283
    op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_powercycle, ["node20744"],
284
                                 query_args, None, "POST",
285
                                 opcodes.OpNodePowercycle)
286
    self.assertEqual(op.node_name, "node20744")
287
    self.assertTrue(op.force)
288

    
289

    
290
class TestGroupAssignNodes(RAPITestCase):
291
  def test(self):
292
    clfactory = _FakeClientFactory(_FakeClient)
293
    query_args = {
294
      "dry-run": ["1"],
295
      "force": ["1"],
296
      }
297
    body_data = {
298
      "nodes": ["n2", "n3"],
299
      }
300
    op = self.getSubmittedOpcode(rlib2.R_2_groups_name_assign_nodes, ["grp-a"],
301
                                 query_args, body_data, "PUT",
302
                                 opcodes.OpGroupAssignNodes)
303
    self.assertEqual(op.group_name, "grp-a")
304
    self.assertEqual(op.nodes, ["n2", "n3"])
305
    self.assertTrue(op.dry_run)
306
    self.assertTrue(op.force)
307

    
308

    
309
class TestInstanceDelete(RAPITestCase):
310
  def test(self):
311
    query_args = {
312
      "dry-run": ["1"],
313
      }
314
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name, ["inst30965"],
315
                                 query_args, {}, "DELETE",
316
                                 opcodes.OpInstanceRemove)
317

    
318
    self.assertEqual(op.instance_name, "inst30965")
319
    self.assertTrue(op.dry_run)
320
    self.assertFalse(op.ignore_failures)
321

    
322

    
323
class TestInstanceInfo(RAPITestCase):
324
  def test(self):
325
    query_args = {
326
      "static": ["1"],
327
      }
328
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_info, ["inst31217"],
329
                                 query_args, {}, "GET",
330
                                 opcodes.OpInstanceQueryData)
331

    
332
    self.assertEqual(op.instances, ["inst31217"])
333
    self.assertTrue(op.static)
334

    
335

    
336
class TestInstanceReboot(RAPITestCase):
337
  def test(self):
338
    query_args = {
339
      "dry-run": ["1"],
340
      "ignore_secondaries": ["1"],
341
      "reason": ["System update"],
342
      }
343
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_reboot, ["inst847"],
344
                                 query_args, {}, "POST",
345
                                 opcodes.OpInstanceReboot)
346

    
347
    self.assertEqual(op.instance_name, "inst847")
348
    self.assertEqual(op.reboot_type, constants.INSTANCE_REBOOT_HARD)
349
    self.assertTrue(op.ignore_secondaries)
350
    self.assertTrue(op.dry_run)
351
    self.assertEqual(op.reason[0][0], constants.OPCODE_REASON_SRC_USER)
352
    self.assertEqual(op.reason[0][1], "System update")
353
    self.assertEqual(op.reason[1][0],
354
                     "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2,
355
                                "instances_name_reboot"))
356
    self.assertEqual(op.reason[1][1], "")
357

    
358

    
359
class TestInstanceStartup(RAPITestCase):
360
  def test(self):
361
    query_args = {
362
      "force": ["1"],
363
      "no_remember": ["1"],
364
      "reason": ["Newly created instance"],
365
      }
366
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_startup,
367
                                 ["inst31083"], query_args, {}, "PUT",
368
                                 opcodes.OpInstanceStartup)
369

    
370
    self.assertEqual(op.instance_name, "inst31083")
371
    self.assertTrue(op.no_remember)
372
    self.assertTrue(op.force)
373
    self.assertFalse(op.dry_run)
374
    self.assertEqual(op.reason[0][0], constants.OPCODE_REASON_SRC_USER)
375
    self.assertEqual(op.reason[0][1], "Newly created instance")
376
    self.assertEqual(op.reason[1][0],
377
                     "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2,
378
                                "instances_name_startup"))
379
    self.assertEqual(op.reason[1][1], "")
380

    
381

    
382
class TestInstanceShutdown(RAPITestCase):
383
  def test(self):
384
    query_args = {
385
      "no_remember": ["0"],
386
      "reason": ["Not used anymore"],
387
      }
388
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_shutdown,
389
                                 ["inst26791"], query_args, {}, "PUT",
390
                                 opcodes.OpInstanceShutdown)
391

    
392
    self.assertEqual(op.instance_name, "inst26791")
393
    self.assertFalse(op.no_remember)
394
    self.assertFalse(op.dry_run)
395
    self.assertEqual(op.reason[0][0], constants.OPCODE_REASON_SRC_USER)
396
    self.assertEqual(op.reason[0][1], "Not used anymore")
397
    self.assertEqual(op.reason[1][0],
398
                     "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2,
399
                                "instances_name_shutdown"))
400
    self.assertEqual(op.reason[1][1], "")
401

    
402

    
403
class TestInstanceActivateDisks(RAPITestCase):
404
  def test(self):
405
    query_args = {
406
      "ignore_size": ["1"],
407
      }
408
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_activate_disks,
409
                                 ["xyz"], query_args, {}, "PUT",
410
                                 opcodes.OpInstanceActivateDisks)
411

    
412
    self.assertEqual(op.instance_name, "xyz")
413
    self.assertTrue(op.ignore_size)
414
    self.assertFalse(op.dry_run)
415

    
416

    
417
class TestInstanceDeactivateDisks(RAPITestCase):
418
  def test(self):
419
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_deactivate_disks,
420
                                 ["inst22357"], {}, {}, "PUT",
421
                                 opcodes.OpInstanceDeactivateDisks)
422

    
423
    self.assertEqual(op.instance_name, "inst22357")
424
    self.assertFalse(op.dry_run)
425
    self.assertFalse(op.force)
426

    
427

    
428
class TestInstanceRecreateDisks(RAPITestCase):
429
  def test(self):
430
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_recreate_disks,
431
                                 ["inst22357"], {}, {}, "POST",
432
                                 opcodes.OpInstanceRecreateDisks)
433

    
434
    self.assertEqual(op.instance_name, "inst22357")
435
    self.assertFalse(op.dry_run)
436
    self.assertFalse(hasattr(op, "force"))
437

    
438

    
439
class TestInstanceFailover(RAPITestCase):
440
  def test(self):
441
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_failover,
442
                                 ["inst12794"], {}, {}, "PUT",
443
                                 opcodes.OpInstanceFailover)
444

    
445
    self.assertEqual(op.instance_name, "inst12794")
446
    self.assertFalse(op.dry_run)
447
    self.assertFalse(hasattr(op, "force"))
448

    
449

    
450
class TestInstanceDiskGrow(RAPITestCase):
451
  def test(self):
452
    data = {
453
      "amount": 1024,
454
      }
455
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_disk_grow,
456
                                 ["inst10742", "3"], {}, data, "POST",
457
                                 opcodes.OpInstanceGrowDisk)
458

    
459
    self.assertEqual(op.instance_name, "inst10742")
460
    self.assertEqual(op.disk, 3)
461
    self.assertEqual(op.amount, 1024)
462
    self.assertFalse(op.dry_run)
463
    self.assertFalse(hasattr(op, "force"))
464

    
465

    
466
class TestInstanceModify(RAPITestCase):
467
  def testCustomParamRename(self):
468
    name = "instant_instance"
469
    data = {
470
      "custom_beparams": {},
471
      "custom_hvparams": {},
472
      }
473

    
474
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_modify, [name], {},
475
                                 data, "PUT", opcodes.OpInstanceSetParams)
476

    
477
    self.assertEqual(op.beparams, {})
478
    self.assertEqual(op.hvparams, {})
479

    
480
    # Define both
481
    data["beparams"] = {}
482
    assert "beparams" in data and "custom_beparams" in data
483
    handler = _CreateHandler(rlib2.R_2_instances_name_modify, [name], {}, data,
484
                             self._clfactory)
485
    self.assertRaises(http.HttpBadRequest, handler.PUT)
486

    
487

    
488
class TestBackupPrepare(RAPITestCase):
489
  def test(self):
490
    query_args = {
491
      "mode": constants.EXPORT_MODE_REMOTE,
492
      }
493
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_prepare_export,
494
                                 ["inst17925"], query_args, {}, "PUT",
495
                                 opcodes.OpBackupPrepare)
496

    
497
    self.assertEqual(op.instance_name, "inst17925")
498
    self.assertEqual(op.mode, constants.EXPORT_MODE_REMOTE)
499
    self.assertFalse(op.dry_run)
500
    self.assertFalse(hasattr(op, "force"))
501

    
502

    
503
class TestGroupRemove(RAPITestCase):
504
  def test(self):
505
    op = self.getSubmittedOpcode(rlib2.R_2_groups_name, ["grp28575"], {}, {},
506
                                 "DELETE", opcodes.OpGroupRemove)
507

    
508
    self.assertEqual(op.group_name, "grp28575")
509
    self.assertFalse(op.dry_run)
510
    self.assertFalse(hasattr(op, "force"))
511

    
512

    
513
class TestStorageQuery(RAPITestCase):
514
  def test(self):
515
    query_args = {
516
      "storage_type": constants.ST_LVM_PV,
517
      "output_fields": "name,other",
518
      }
519
    op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_storage, ["node21075"],
520
                                 query_args, {}, "GET",
521
                                 opcodes.OpNodeQueryStorage)
522

    
523
    self.assertEqual(op.nodes, ["node21075"])
524
    self.assertEqual(op.storage_type, constants.ST_LVM_PV)
525
    self.assertEqual(op.output_fields, ["name", "other"])
526
    self.assertFalse(op.dry_run)
527
    self.assertFalse(hasattr(op, "force"))
528

    
529
  def testErrors(self):
530
    # storage type which does not support space reporting
531
    queryargs = {
532
      "storage_type": constants.ST_DISKLESS,
533
      }
534
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage,
535
                             ["node21273"], queryargs, {}, self._clfactory)
536
    self.assertRaises(http.HttpBadRequest, handler.GET)
537

    
538
    queryargs = {
539
      "storage_type": constants.ST_LVM_VG,
540
      }
541
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage,
542
                             ["node21273"], queryargs, {}, self._clfactory)
543
    self.assertRaises(http.HttpBadRequest, handler.GET)
544

    
545
    queryargs = {
546
      "storage_type": "##unknown_storage##",
547
      "output_fields": "name,other",
548
      }
549
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage,
550
                             ["node10315"], queryargs, {}, self._clfactory)
551
    self.assertRaises(http.HttpBadRequest, handler.GET)
552

    
553

    
554
class TestStorageModify(RAPITestCase):
555
  def test(self):
556
    clfactory = _FakeClientFactory(_FakeClient)
557

    
558
    for allocatable in [None, "1", "0"]:
559
      query_args = {
560
        "storage_type": constants.ST_LVM_VG,
561
        "name": "pv-a",
562
        }
563

    
564
      if allocatable is not None:
565
        query_args["allocatable"] = allocatable
566

    
567
      op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_storage_modify,
568
                                   ["node9292"], query_args, {}, "PUT",
569
                                   opcodes.OpNodeModifyStorage)
570

    
571
      self.assertEqual(op.node_name, "node9292")
572
      self.assertEqual(op.storage_type, constants.ST_LVM_VG)
573
      self.assertEqual(op.name, "pv-a")
574
      if allocatable is None:
575
        self.assertFalse(op.changes)
576
      else:
577
        assert allocatable in ("0", "1")
578
        self.assertEqual(op.changes, {
579
          constants.SF_ALLOCATABLE: (allocatable == "1"),
580
          })
581
      self.assertFalse(op.dry_run)
582
      self.assertFalse(hasattr(op, "force"))
583

    
584
  def testErrors(self):
585
    # No storage type
586
    queryargs = {
587
      "name": "xyz",
588
      }
589
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage_modify,
590
                             ["node26016"], queryargs, {}, self._clfactory)
591
    self.assertRaises(http.HttpBadRequest, handler.PUT)
592

    
593
    # No name
594
    queryargs = {
595
      "storage_type": constants.ST_LVM_VG,
596
      }
597
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage_modify,
598
                             ["node21218"], queryargs, {}, self._clfactory)
599
    self.assertRaises(http.HttpBadRequest, handler.PUT)
600

    
601
    # Invalid value
602
    queryargs = {
603
      "storage_type": constants.ST_LVM_VG,
604
      "name": "pv-b",
605
      "allocatable": "noint",
606
      }
607
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage_modify,
608
                             ["node30685"], queryargs, {}, self._clfactory)
609
    self.assertRaises(http.HttpBadRequest, handler.PUT)
610

    
611

    
612
class TestStorageRepair(RAPITestCase):
613
  def test(self):
614
    query_args = {
615
      "storage_type": constants.ST_LVM_PV,
616
      "name": "pv16611",
617
      }
618
    op = self.getSubmittedOpcode(rlib2.R_2_nodes_name_storage_repair,
619
                                 ["node19265"], query_args, {}, "PUT",
620
                                 opcodes.OpRepairNodeStorage)
621

    
622
    self.assertEqual(op.node_name, "node19265")
623
    self.assertEqual(op.storage_type, constants.ST_LVM_PV)
624
    self.assertEqual(op.name, "pv16611")
625
    self.assertFalse(op.dry_run)
626
    self.assertFalse(hasattr(op, "force"))
627

    
628
  def testErrors(self):
629
    # No storage type
630
    queryargs = {
631
      "name": "xyz",
632
      }
633
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage_repair,
634
                             ["node11275"], queryargs, {}, self._clfactory)
635
    self.assertRaises(http.HttpBadRequest, handler.PUT)
636

    
637
    # No name
638
    queryargs = {
639
      "storage_type": constants.ST_LVM_VG,
640
      }
641
    handler = _CreateHandler(rlib2.R_2_nodes_name_storage_repair,
642
                             ["node21218"], queryargs, {}, self._clfactory)
643
    self.assertRaises(http.HttpBadRequest, handler.PUT)
644

    
645

    
646
class TestTags(RAPITestCase):
647
  TAG_HANDLERS = [
648
    rlib2.R_2_instances_name_tags,
649
    rlib2.R_2_nodes_name_tags,
650
    rlib2.R_2_groups_name_tags,
651
    rlib2.R_2_tags,
652
    ]
653

    
654
  def testSetAndDelete(self):
655
    for method, opcls in [("PUT", opcodes.OpTagsSet),
656
                          ("DELETE", opcodes.OpTagsDel)]:
657
      for idx, handler in enumerate(self.TAG_HANDLERS):
658
        dry_run = bool(idx % 2)
659
        name = "test%s" % idx
660
        queryargs = {
661
          "tag": ["foo", "bar", "baz"],
662
          "dry-run": str(int(dry_run)),
663
          }
664

    
665
        op = self.getSubmittedOpcode(handler, [name], queryargs, {}, method,
666
                                     opcls)
667

    
668
        self.assertEqual(op.kind, handler.TAG_LEVEL)
669
        if handler.TAG_LEVEL == constants.TAG_CLUSTER:
670
          self.assertTrue(op.name is None)
671
        else:
672
          self.assertEqual(op.name, name)
673
        self.assertEqual(op.tags, ["foo", "bar", "baz"])
674
        self.assertEqual(op.dry_run, dry_run)
675
        self.assertFalse(hasattr(op, "force"))
676

    
677

    
678
class TestInstanceCreation(RAPITestCase):
679
  def test(self):
680
    name = "inst863.example.com"
681

    
682
    disk_variants = [
683
      # No disks
684
      [],
685

    
686
      # Two disks
687
      [{"size": 5, }, {"size": 100, }],
688

    
689
      # Disk with mode
690
      [{"size": 123, "mode": constants.DISK_RDWR, }],
691
      ]
692

    
693
    nic_variants = [
694
      # No NIC
695
      [],
696

    
697
      # Three NICs
698
      [{}, {}, {}],
699

    
700
      # Two NICs
701
      [
702
        { "ip": "192.0.2.6", "mode": constants.NIC_MODE_ROUTED,
703
          "mac": "01:23:45:67:68:9A",
704
        },
705
        { "mode": constants.NIC_MODE_BRIDGED, "link": "br1" },
706
      ],
707
      ]
708

    
709
    beparam_variants = [
710
      None,
711
      {},
712
      { constants.BE_VCPUS: 2, },
713
      { constants.BE_MAXMEM: 200, },
714
      { constants.BE_MEMORY: 256, },
715
      { constants.BE_VCPUS: 2,
716
        constants.BE_MAXMEM: 1024,
717
        constants.BE_MINMEM: 1024,
718
        constants.BE_AUTO_BALANCE: True,
719
        constants.BE_ALWAYS_FAILOVER: True, }
720
      ]
721

    
722
    hvparam_variants = [
723
      None,
724
      { constants.HV_BOOT_ORDER: "anc", },
725
      { constants.HV_KERNEL_PATH: "/boot/fookernel",
726
        constants.HV_ROOT_PATH: "/dev/hda1", },
727
      ]
728

    
729
    for mode in [constants.INSTANCE_CREATE, constants.INSTANCE_IMPORT]:
730
      for nics in nic_variants:
731
        for disk_template in constants.DISK_TEMPLATES:
732
          for disks in disk_variants:
733
            for beparams in beparam_variants:
734
              for hvparams in hvparam_variants:
735
                for dry_run in [False, True]:
736
                  query_args = {
737
                    "dry-run": str(int(dry_run)),
738
                    }
739

    
740
                  data = {
741
                    rlib2._REQ_DATA_VERSION: 1,
742
                    "name": name,
743
                    "hypervisor": constants.HT_FAKE,
744
                    "disks": disks,
745
                    "nics": nics,
746
                    "mode": mode,
747
                    "disk_template": disk_template,
748
                    "os": "debootstrap",
749
                    }
750

    
751
                  if beparams is not None:
752
                    data["beparams"] = beparams
753

    
754
                  if hvparams is not None:
755
                    data["hvparams"] = hvparams
756

    
757
                  op = self.getSubmittedOpcode(
758
                    rlib2.R_2_instances, [], query_args, data, "POST",
759
                    opcodes.OpInstanceCreate
760
                  )
761

    
762
                  self.assertEqual(op.instance_name, name)
763
                  self.assertEqual(op.mode, mode)
764
                  self.assertEqual(op.disk_template, disk_template)
765
                  self.assertEqual(op.dry_run, dry_run)
766
                  self.assertEqual(len(op.disks), len(disks))
767
                  self.assertEqual(len(op.nics), len(nics))
768

    
769
                  for opdisk, disk in zip(op.disks, disks):
770
                    for key in constants.IDISK_PARAMS:
771
                      self.assertEqual(opdisk.get(key), disk.get(key))
772
                    self.assertFalse("unknown" in opdisk)
773

    
774
                  for opnic, nic in zip(op.nics, nics):
775
                    for key in constants.INIC_PARAMS:
776
                      self.assertEqual(opnic.get(key), nic.get(key))
777
                    self.assertFalse("unknown" in opnic)
778
                    self.assertFalse("foobar" in opnic)
779

    
780
                  if beparams is None:
781
                    self.assertTrue(op.beparams in [None, {}])
782
                  else:
783
                    self.assertEqualValues(op.beparams, beparams)
784

    
785
                  if hvparams is None:
786
                    self.assertTrue(op.hvparams in [None, {}])
787
                  else:
788
                    self.assertEqualValues(op.hvparams, hvparams)
789

    
790
  def testLegacyName(self):
791
    name = "inst29128.example.com"
792
    data = {
793
      rlib2._REQ_DATA_VERSION: 1,
794
      "name": name,
795
      "disks": [],
796
      "nics": [],
797
      "mode": constants.INSTANCE_CREATE,
798
      "disk_template": constants.DT_PLAIN,
799
      }
800

    
801
    op = self.getSubmittedOpcode(rlib2.R_2_instances, [], {}, data, "POST",
802
                                 opcodes.OpInstanceCreate)
803

    
804
    self.assertEqual(op.instance_name, name)
805
    self.assertFalse(hasattr(op, "name"))
806
    self.assertFalse(op.dry_run)
807

    
808
    # Define both
809
    data["instance_name"] = "other.example.com"
810
    assert "name" in data and "instance_name" in data
811
    handler = _CreateHandler(rlib2.R_2_instances, [], {}, data, self._clfactory)
812
    self.assertRaises(http.HttpBadRequest, handler.POST)
813
    self.assertNoNextClient()
814

    
815
  def testLegacyOs(self):
816
    name = "inst4673.example.com"
817
    os = "linux29206"
818
    data = {
819
      rlib2._REQ_DATA_VERSION: 1,
820
      "name": name,
821
      "os_type": os,
822
      "disks": [],
823
      "nics": [],
824
      "mode": constants.INSTANCE_CREATE,
825
      "disk_template": constants.DT_PLAIN,
826
      }
827

    
828
    op = self.getSubmittedOpcode(rlib2.R_2_instances, [], {}, data, "POST",
829
                                 opcodes.OpInstanceCreate)
830

    
831
    self.assertEqual(op.instance_name, name)
832
    self.assertEqual(op.os_type, os)
833
    self.assertFalse(hasattr(op, "os"))
834
    self.assertFalse(op.dry_run)
835

    
836
    # Define both
837
    data["os"] = "linux9584"
838
    assert "os" in data and "os_type" in data
839
    handler = _CreateHandler(rlib2.R_2_instances, [], {}, data, self._clfactory)
840
    self.assertRaises(http.HttpBadRequest, handler.POST)
841

    
842
  def testErrors(self):
843
    # Test all required fields
844
    reqfields = {
845
      rlib2._REQ_DATA_VERSION: 1,
846
      "name": "inst1.example.com",
847
      "disks": [],
848
      "nics": [],
849
      "mode": constants.INSTANCE_CREATE,
850
      }
851

    
852
    for name in reqfields.keys():
853
      data = dict(i for i in reqfields.iteritems() if i[0] != name)
854

    
855
      handler = _CreateHandler(rlib2.R_2_instances, [], {}, data,
856
                               self._clfactory)
857
      self.assertRaises(http.HttpBadRequest, handler.POST)
858
      self.assertNoNextClient()
859

    
860
    # Invalid disks and nics
861
    for field in ["disks", "nics"]:
862
      invalid_values = [None, 1, "", {}, [1, 2, 3], ["hda1", "hda2"],
863
                        [{"_unknown_": False, }]]
864

    
865
      for invvalue in invalid_values:
866
        data = reqfields.copy()
867
        data[field] = invvalue
868
        handler = _CreateHandler(rlib2.R_2_instances, [], {}, data,
869
                                 self._clfactory)
870
        self.assertRaises(http.HttpBadRequest, handler.POST)
871
        self.assertNoNextClient()
872

    
873
  def testVersion(self):
874
    # No version field
875
    data = {
876
      "name": "inst1.example.com",
877
      "disks": [],
878
      "nics": [],
879
      "mode": constants.INSTANCE_CREATE,
880
      "disk_template": constants.DT_PLAIN,
881
      }
882

    
883
    handler = _CreateHandler(rlib2.R_2_instances, [], {}, data, self._clfactory)
884
    self.assertRaises(http.HttpBadRequest, handler.POST)
885

    
886
    # Old and incorrect versions
887
    for version in [0, -1, 10483, "Hello World"]:
888
      data[rlib2._REQ_DATA_VERSION] = version
889

    
890
      handler = _CreateHandler(rlib2.R_2_instances, [], {}, data,
891
                               self._clfactory)
892
      self.assertRaises(http.HttpBadRequest, handler.POST)
893
      self.assertNoNextClient()
894

    
895
    # Correct version
896
    data[rlib2._REQ_DATA_VERSION] = 1
897
    self.getSubmittedOpcode(rlib2.R_2_instances, [], {}, data, "POST",
898
                            opcodes.OpInstanceCreate)
899

    
900

    
901
class TestBackupExport(RAPITestCase):
902
  def test(self):
903
    name = "instmoo"
904
    data = {
905
      "mode": constants.EXPORT_MODE_REMOTE,
906
      "destination": [(1, 2, 3), (99, 99, 99)],
907
      "shutdown": True,
908
      "remove_instance": True,
909
      "x509_key_name": ["name", "hash"],
910
      "destination_x509_ca": "---cert---"
911
      }
912

    
913
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_export, [name], {},
914
                                 data, "PUT", opcodes.OpBackupExport)
915

    
916
    self.assertEqual(op.instance_name, name)
917
    self.assertEqual(op.mode, constants.EXPORT_MODE_REMOTE)
918
    self.assertEqual(op.target_node, [(1, 2, 3), (99, 99, 99)])
919
    self.assertEqual(op.shutdown, True)
920
    self.assertEqual(op.remove_instance, True)
921
    self.assertEqual(op.x509_key_name, ["name", "hash"])
922
    self.assertEqual(op.destination_x509_ca, "---cert---")
923
    self.assertFalse(op.dry_run)
924
    self.assertFalse(hasattr(op, "force"))
925

    
926
  def testDefaults(self):
927
    name = "inst1"
928
    data = {
929
      "destination": "node2",
930
      "shutdown": False,
931
      }
932

    
933
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_export, [name], {},
934
                                 data, "PUT", opcodes.OpBackupExport)
935

    
936
    self.assertEqual(op.instance_name, name)
937
    self.assertEqual(op.target_node, "node2")
938
    self.assertEqual(op.mode, "local")
939
    self.assertFalse(op.remove_instance)
940
    self.assertFalse(hasattr(op, "destination"))
941
    self.assertFalse(op.dry_run)
942
    self.assertFalse(hasattr(op, "force"))
943

    
944
  def testErrors(self):
945
    clfactory = _FakeClientFactory(_FakeClient)
946

    
947
    for value in ["True", "False"]:
948
      data = {
949
        "remove_instance": value,
950
        }
951
      handler = _CreateHandler(rlib2.R_2_instances_name_export, ["err1"], {},
952
                               data, self._clfactory)
953
      self.assertRaises(http.HttpBadRequest, handler.PUT)
954

    
955

    
956
class TestInstanceMigrate(RAPITestCase):
957
  def test(self):
958
    name = "instYooho6ek"
959

    
960
    for cleanup in [False, True]:
961
      for mode in constants.HT_MIGRATION_MODES:
962
        data = {
963
          "cleanup": cleanup,
964
          "mode": mode,
965
          }
966

    
967
        op = self.getSubmittedOpcode(rlib2.R_2_instances_name_migrate, [name],
968
                                     {}, data, "PUT", opcodes.OpInstanceMigrate)
969

    
970
        self.assertEqual(op.instance_name, name)
971
        self.assertEqual(op.mode, mode)
972
        self.assertEqual(op.cleanup, cleanup)
973
        self.assertFalse(op.dry_run)
974
        self.assertFalse(hasattr(op, "force"))
975

    
976
  def testDefaults(self):
977
    name = "instnohZeex0"
978

    
979
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_migrate, [name], {},
980
                                 {}, "PUT", opcodes.OpInstanceMigrate)
981

    
982
    self.assertEqual(op.instance_name, name)
983
    self.assertTrue(op.mode is None)
984
    self.assertFalse(op.cleanup)
985
    self.assertFalse(op.dry_run)
986
    self.assertFalse(hasattr(op, "force"))
987

    
988

    
989
class TestParseRenameInstanceRequest(RAPITestCase):
990
  def test(self):
991
    name = "instij0eeph7"
992

    
993
    for new_name in ["ua0aiyoo", "fai3ongi"]:
994
      for ip_check in [False, True]:
995
        for name_check in [False, True]:
996
          data = {
997
            "new_name": new_name,
998
            "ip_check": ip_check,
999
            "name_check": name_check,
1000
            }
1001

    
1002
          op = self.getSubmittedOpcode(rlib2.R_2_instances_name_rename, [name],
1003
                                       {}, data, "PUT",
1004
                                       opcodes.OpInstanceRename)
1005

    
1006
          self.assertEqual(op.instance_name, name)
1007
          self.assertEqual(op.new_name, new_name)
1008
          self.assertEqual(op.ip_check, ip_check)
1009
          self.assertEqual(op.name_check, name_check)
1010
          self.assertFalse(op.dry_run)
1011
          self.assertFalse(hasattr(op, "force"))
1012

    
1013
  def testDefaults(self):
1014
    name = "instahchie3t"
1015

    
1016
    for new_name in ["thag9mek", "quees7oh"]:
1017
      data = {
1018
        "new_name": new_name,
1019
        }
1020

    
1021
      op = self.getSubmittedOpcode(rlib2.R_2_instances_name_rename, [name],
1022
                                   {}, data, "PUT", opcodes.OpInstanceRename)
1023

    
1024
      self.assertEqual(op.instance_name, name)
1025
      self.assertEqual(op.new_name, new_name)
1026
      self.assertTrue(op.ip_check)
1027
      self.assertTrue(op.name_check)
1028
      self.assertFalse(op.dry_run)
1029
      self.assertFalse(hasattr(op, "force"))
1030

    
1031

    
1032
class TestParseModifyInstanceRequest(RAPITestCase):
1033
  def test(self):
1034
    name = "instush8gah"
1035

    
1036
    test_disks = [
1037
      [],
1038
      [(1, { constants.IDISK_MODE: constants.DISK_RDWR, })],
1039
      ]
1040

    
1041
    for osparams in [{}, { "some": "value", "other": "Hello World", }]:
1042
      for hvparams in [{}, { constants.HV_KERNEL_PATH: "/some/kernel", }]:
1043
        for beparams in [{}, { constants.BE_MAXMEM: 128, }]:
1044
          for force in [False, True]:
1045
            for nics in [[], [(0, { constants.INIC_IP: "192.0.2.1", })]]:
1046
              for disks in test_disks:
1047
                for disk_template in constants.DISK_TEMPLATES:
1048
                  data = {
1049
                    "osparams": osparams,
1050
                    "hvparams": hvparams,
1051
                    "beparams": beparams,
1052
                    "nics": nics,
1053
                    "disks": disks,
1054
                    "force": force,
1055
                    "disk_template": disk_template,
1056
                    }
1057

    
1058
                  op = self.getSubmittedOpcode(
1059
                    rlib2.R_2_instances_name_modify, [name], {}, data, "PUT",
1060
                    opcodes.OpInstanceSetParams
1061
                  )
1062

    
1063
                  self.assertEqual(op.instance_name, name)
1064
                  self.assertEqual(op.hvparams, hvparams)
1065
                  self.assertEqual(op.beparams, beparams)
1066
                  self.assertEqual(op.osparams, osparams)
1067
                  self.assertEqual(op.force, force)
1068
                  self.assertEqual(op.nics, nics)
1069
                  self.assertEqual(op.disks, disks)
1070
                  self.assertEqual(op.disk_template, disk_template)
1071
                  self.assertTrue(op.remote_node is None)
1072
                  self.assertTrue(op.os_name is None)
1073
                  self.assertFalse(op.force_variant)
1074
                  self.assertFalse(op.dry_run)
1075

    
1076
  def testDefaults(self):
1077
    name = "instir8aish31"
1078

    
1079
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_modify, [name], {},
1080
                                 {}, "PUT", opcodes.OpInstanceSetParams)
1081

    
1082
    for i in ["hvparams", "beparams", "osparams", "force", "nics", "disks",
1083
              "disk_template", "remote_node", "os_name", "force_variant"]:
1084
      self.assertTrue(hasattr(op, i))
1085

    
1086

    
1087
class TestParseInstanceReinstallRequest(testutils.GanetiTestCase):
1088
  def setUp(self):
1089
    testutils.GanetiTestCase.setUp(self)
1090

    
1091
    self.Parse = rlib2._ParseInstanceReinstallRequest
1092

    
1093
  def _Check(self, ops, name):
1094
    expcls = [
1095
      opcodes.OpInstanceShutdown,
1096
      opcodes.OpInstanceReinstall,
1097
      opcodes.OpInstanceStartup,
1098
      ]
1099

    
1100
    self.assert_(compat.all(isinstance(op, exp)
1101
                            for op, exp in zip(ops, expcls)))
1102
    self.assert_(compat.all(op.instance_name == name for op in ops))
1103

    
1104
  def test(self):
1105
    name = "shoo0tihohma"
1106

    
1107
    ops = self.Parse(name, {"os": "sys1", "start": True,})
1108
    self.assertEqual(len(ops), 3)
1109
    self._Check(ops, name)
1110
    self.assertEqual(ops[1].os_type, "sys1")
1111
    self.assertFalse(ops[1].osparams)
1112

    
1113
    ops = self.Parse(name, {"os": "sys2", "start": False,})
1114
    self.assertEqual(len(ops), 2)
1115
    self._Check(ops, name)
1116
    self.assertEqual(ops[1].os_type, "sys2")
1117

    
1118
    osparams = {
1119
      "reformat": "1",
1120
      }
1121
    ops = self.Parse(name, {"os": "sys4035", "start": True,
1122
                            "osparams": osparams,})
1123
    self.assertEqual(len(ops), 3)
1124
    self._Check(ops, name)
1125
    self.assertEqual(ops[1].os_type, "sys4035")
1126
    self.assertEqual(ops[1].osparams, osparams)
1127

    
1128
  def testDefaults(self):
1129
    name = "noolee0g"
1130

    
1131
    ops = self.Parse(name, {"os": "linux1"})
1132
    self.assertEqual(len(ops), 3)
1133
    self._Check(ops, name)
1134
    self.assertEqual(ops[1].os_type, "linux1")
1135
    self.assertFalse(ops[1].osparams)
1136

    
1137
  def testErrors(self):
1138
    self.assertRaises(http.HttpBadRequest, self.Parse,
1139
                      "foo", "not a dictionary")
1140

    
1141

    
1142
class TestGroupRename(RAPITestCase):
1143
  def test(self):
1144
    name = "group608242564"
1145
    data = {
1146
      "new_name": "ua0aiyoo15112",
1147
      }
1148

    
1149
    op = self.getSubmittedOpcode(rlib2.R_2_groups_name_rename, [name], {}, data,
1150
                                 "PUT", opcodes.OpGroupRename)
1151

    
1152
    self.assertEqual(op.group_name, name)
1153
    self.assertEqual(op.new_name, "ua0aiyoo15112")
1154
    self.assertFalse(op.dry_run)
1155

    
1156
  def testDryRun(self):
1157
    name = "group28548"
1158
    query_args = {
1159
      "dry-run": ["1"],
1160
      }
1161
    data = {
1162
      "new_name": "ua0aiyoo",
1163
      }
1164

    
1165
    op = self.getSubmittedOpcode(rlib2.R_2_groups_name_rename, [name],
1166
                                 query_args, data, "PUT", opcodes.OpGroupRename)
1167

    
1168
    self.assertEqual(op.group_name, name)
1169
    self.assertEqual(op.new_name, "ua0aiyoo")
1170
    self.assertTrue(op.dry_run)
1171

    
1172

    
1173
class TestInstanceReplaceDisks(RAPITestCase):
1174
  def test(self):
1175
    name = "inst22568"
1176

    
1177
    for disks in [range(1, 4), "1,2,3", "1, 2, 3"]:
1178
      data = {
1179
        "mode": constants.REPLACE_DISK_SEC,
1180
        "disks": disks,
1181
        "iallocator": "myalloc",
1182
        }
1183

    
1184
      op = self.getSubmittedOpcode(rlib2.R_2_instances_name_replace_disks,
1185
                                   [name], {}, data, "POST",
1186
                                   opcodes.OpInstanceReplaceDisks)
1187

    
1188
      self.assertEqual(op.instance_name, name)
1189
      self.assertEqual(op.mode, constants.REPLACE_DISK_SEC)
1190
      self.assertEqual(op.disks, [1, 2, 3])
1191
      self.assertEqual(op.iallocator, "myalloc")
1192

    
1193
  def testDefaults(self):
1194
    name = "inst11413"
1195
    data = {
1196
      "mode": constants.REPLACE_DISK_AUTO,
1197
      }
1198

    
1199
    op = self.getSubmittedOpcode(rlib2.R_2_instances_name_replace_disks,
1200
                                 [name], {}, data, "POST",
1201
                                 opcodes.OpInstanceReplaceDisks)
1202

    
1203
    self.assertEqual(op.instance_name, name)
1204
    self.assertEqual(op.mode, constants.REPLACE_DISK_AUTO)
1205
    self.assertTrue(op.iallocator is None)
1206
    self.assertEqual(op.disks, [])
1207

    
1208
  def testNoDisks(self):
1209
    handler = _CreateHandler(rlib2.R_2_instances_name_replace_disks,
1210
                             ["inst20661"], {}, {}, self._clfactory)
1211
    self.assertRaises(http.HttpBadRequest, handler.POST)
1212

    
1213
    for disks in [None, "", {}]:
1214
      handler = _CreateHandler(rlib2.R_2_instances_name_replace_disks,
1215
                               ["inst20661"], {}, {
1216
        "disks": disks,
1217
        }, self._clfactory)
1218
      self.assertRaises(http.HttpBadRequest, handler.POST)
1219

    
1220
  def testWrong(self):
1221
    data = {
1222
      "mode": constants.REPLACE_DISK_AUTO,
1223
      "disks": "hello world",
1224
      }
1225

    
1226
    handler = _CreateHandler(rlib2.R_2_instances_name_replace_disks,
1227
                             ["foo"], {}, data, self._clfactory)
1228
    self.assertRaises(http.HttpBadRequest, handler.POST)
1229

    
1230

    
1231
class TestGroupModify(RAPITestCase):
1232
  def test(self):
1233
    name = "group6002"
1234

    
1235
    for policy in constants.VALID_ALLOC_POLICIES:
1236
      data = {
1237
        "alloc_policy": policy,
1238
        }
1239

    
1240
      op = self.getSubmittedOpcode(rlib2.R_2_groups_name_modify, [name], {},
1241
                                   data, "PUT", opcodes.OpGroupSetParams)
1242

    
1243
      self.assertEqual(op.group_name, name)
1244
      self.assertEqual(op.alloc_policy, policy)
1245
      self.assertFalse(op.dry_run)
1246

    
1247
  def testUnknownPolicy(self):
1248
    data = {
1249
      "alloc_policy": "_unknown_policy_",
1250
      }
1251

    
1252
    handler = _CreateHandler(rlib2.R_2_groups_name_modify, ["xyz"], {}, data,
1253
                             self._clfactory)
1254
    self.assertRaises(http.HttpBadRequest, handler.PUT)
1255
    self.assertNoNextClient()
1256

    
1257
  def testDefaults(self):
1258
    name = "group6679"
1259

    
1260
    op = self.getSubmittedOpcode(rlib2.R_2_groups_name_modify, [name], {}, {},
1261
                                 "PUT", opcodes.OpGroupSetParams)
1262

    
1263
    self.assertEqual(op.group_name, name)
1264
    self.assertTrue(op.alloc_policy is None)
1265
    self.assertFalse(op.dry_run)
1266

    
1267
  def testCustomParamRename(self):
1268
    name = "groupie"
1269
    data = {
1270
      "custom_diskparams": {},
1271
      "custom_ipolicy": {},
1272
      "custom_ndparams": {},
1273
      }
1274

    
1275
    op = self.getSubmittedOpcode(rlib2.R_2_groups_name_modify, [name], {}, data,
1276
                                 "PUT", opcodes.OpGroupSetParams)
1277

    
1278
    self.assertEqual(op.diskparams, {})
1279
    self.assertEqual(op.ipolicy, {})
1280
    self.assertEqual(op.ndparams, {})
1281

    
1282
    # Define both
1283
    data["diskparams"] = {}
1284
    assert "diskparams" in data and "custom_diskparams" in data
1285
    handler = _CreateHandler(rlib2.R_2_groups_name_modify, [name], {}, data,
1286
                             self._clfactory)
1287
    self.assertRaises(http.HttpBadRequest, handler.PUT)
1288

    
1289

    
1290
class TestGroupAdd(RAPITestCase):
1291
  def test(self):
1292
    name = "group3618"
1293

    
1294
    for policy in constants.VALID_ALLOC_POLICIES:
1295
      data = {
1296
        "group_name": name,
1297
        "alloc_policy": policy,
1298
        }
1299

    
1300
      op = self.getSubmittedOpcode(rlib2.R_2_groups, [], {}, data, "POST",
1301
                                   opcodes.OpGroupAdd)
1302

    
1303
      self.assertEqual(op.group_name, name)
1304
      self.assertEqual(op.alloc_policy, policy)
1305
      self.assertFalse(op.dry_run)
1306

    
1307
  def testUnknownPolicy(self):
1308
    data = {
1309
      "alloc_policy": "_unknown_policy_",
1310
      }
1311

    
1312
    handler = _CreateHandler(rlib2.R_2_groups, [], {}, data, self._clfactory)
1313
    self.assertRaises(http.HttpBadRequest, handler.POST)
1314
    self.assertNoNextClient()
1315

    
1316
  def testDefaults(self):
1317
    name = "group15395"
1318
    data = {
1319
      "group_name": name,
1320
      }
1321

    
1322
    op = self.getSubmittedOpcode(rlib2.R_2_groups, [], {}, data, "POST",
1323
                                 opcodes.OpGroupAdd)
1324

    
1325
    self.assertEqual(op.group_name, name)
1326
    self.assertTrue(op.alloc_policy is None)
1327
    self.assertFalse(op.dry_run)
1328

    
1329
  def testLegacyName(self):
1330
    name = "group29852"
1331
    query_args = {
1332
      "dry-run": ["1"],
1333
      }
1334
    data = {
1335
      "name": name,
1336
      }
1337

    
1338
    op = self.getSubmittedOpcode(rlib2.R_2_groups, [], query_args, data, "POST",
1339
                                 opcodes.OpGroupAdd)
1340

    
1341
    self.assertEqual(op.group_name, name)
1342
    self.assertTrue(op.alloc_policy is None)
1343
    self.assertTrue(op.dry_run)
1344

    
1345

    
1346
class TestNodeRole(RAPITestCase):
1347
  def test(self):
1348
    for role in rlib2._NR_MAP.values():
1349
      handler = _CreateHandler(rlib2.R_2_nodes_name_role,
1350
                               ["node-z"], {}, role, self._clfactory)
1351
      if role == rlib2._NR_MASTER:
1352
        self.assertRaises(http.HttpBadRequest, handler.PUT)
1353
      else:
1354
        job_id = handler.PUT()
1355

    
1356
        cl = self._clfactory.GetNextClient()
1357
        self.assertNoNextClient()
1358

    
1359
        (exp_job_id, (op, )) = cl.GetNextSubmittedJob()
1360
        self.assertEqual(job_id, exp_job_id)
1361
        self.assertTrue(isinstance(op, opcodes.OpNodeSetParams))
1362
        self.assertEqual(op.node_name, "node-z")
1363
        self.assertFalse(op.force)
1364
        self.assertFalse(op.dry_run)
1365

    
1366
        if role == rlib2._NR_REGULAR:
1367
          self.assertFalse(op.drained)
1368
          self.assertFalse(op.offline)
1369
          self.assertFalse(op.master_candidate)
1370
        elif role == rlib2._NR_MASTER_CANDIDATE:
1371
          self.assertFalse(op.drained)
1372
          self.assertFalse(op.offline)
1373
          self.assertTrue(op.master_candidate)
1374
        elif role == rlib2._NR_DRAINED:
1375
          self.assertTrue(op.drained)
1376
          self.assertFalse(op.offline)
1377
          self.assertFalse(op.master_candidate)
1378
        elif role == rlib2._NR_OFFLINE:
1379
          self.assertFalse(op.drained)
1380
          self.assertTrue(op.offline)
1381
          self.assertFalse(op.master_candidate)
1382
        else:
1383
          self.fail("Unknown role '%s'" % role)
1384

    
1385
      self.assertRaises(IndexError, cl.GetNextSubmittedJob)
1386

    
1387

    
1388
class TestSimpleResources(RAPITestCase):
1389
  def tearDown(self):
1390
    self.assertNoNextClient()
1391

    
1392
  def testFeatures(self):
1393
    handler = _CreateHandler(rlib2.R_2_features, [], {}, None, self._clfactory)
1394
    self.assertEqual(set(handler.GET()), rlib2.ALL_FEATURES)
1395

    
1396
  def testEmpty(self):
1397
    for cls in [rlib2.R_root, rlib2.R_2]:
1398
      handler = _CreateHandler(cls, [], {}, None, self._clfactory)
1399
      self.assertTrue(handler.GET() is None)
1400

    
1401
  def testVersion(self):
1402
    handler = _CreateHandler(rlib2.R_version, [], {}, None, self._clfactory)
1403
    self.assertEqual(handler.GET(), constants.RAPI_VERSION)
1404

    
1405

    
1406
class TestClusterInfo(unittest.TestCase):
1407
  class _ClusterInfoClient:
1408
    def __init__(self, address=None):
1409
      self.cluster_info = None
1410

    
1411
    def QueryClusterInfo(self):
1412
      assert self.cluster_info is None
1413
      self.cluster_info = {}
1414
      return self.cluster_info
1415

    
1416
  def test(self):
1417
    clfactory = _FakeClientFactory(self._ClusterInfoClient)
1418
    handler = _CreateHandler(rlib2.R_2_info, [], {}, None, clfactory)
1419
    result = handler.GET()
1420
    cl = clfactory.GetNextClient()
1421
    self.assertRaises(IndexError, clfactory.GetNextClient)
1422
    self.assertEqual(result, cl.cluster_info)
1423

    
1424

    
1425
class TestInstancesMultiAlloc(unittest.TestCase):
1426
  def testInstanceUpdate(self):
1427
    clfactory = _FakeClientFactory(_FakeClient)
1428
    data = {
1429
      "instances": [{
1430
        "name": "bar",
1431
        "mode": "create",
1432
        "disks": [{"size": 1024}],
1433
        "disk_template": "plain",
1434
        "nics": [{}],
1435
        }, {
1436
        "name": "foo",
1437
        "mode": "create",
1438
        "disks": [{"size": 1024}],
1439
        "disk_template": "drbd",
1440
        "nics": [{}],
1441
        }],
1442
      }
1443
    handler = _CreateHandler(rlib2.R_2_instances_multi_alloc, [], {}, data,
1444
                             clfactory)
1445

    
1446
    (body, _) = handler.GetPostOpInput()
1447

    
1448
    self.assertTrue(compat.all(
1449
      [isinstance(inst, opcodes.OpInstanceCreate) for inst in body["instances"]]
1450
    ))
1451

    
1452

    
1453
class TestPermissions(unittest.TestCase):
1454
  def testEquality(self):
1455
    self.assertEqual(rlib2.R_2_query.GET_ACCESS, rlib2.R_2_query.PUT_ACCESS)
1456
    self.assertEqual(rlib2.R_2_query.GET_ACCESS,
1457
                     rlib2.R_2_instances_name_console.GET_ACCESS)
1458

    
1459
  def testMethodAccess(self):
1460
    for handler in connector.CONNECTOR.values():
1461
      for method in baserlib._SUPPORTED_METHODS:
1462
        access = baserlib.GetHandlerAccess(handler, method)
1463
        self.assertFalse(access is None)
1464
        self.assertFalse(set(access) - rapi.RAPI_ACCESS_ALL,
1465
                         msg=("Handler '%s' uses unknown access options for"
1466
                              " method %s" % (handler, method)))
1467
        self.assertTrue(rapi.RAPI_ACCESS_READ not in access or
1468
                        rapi.RAPI_ACCESS_WRITE in access,
1469
                        msg=("Handler '%s' gives query, but not write access"
1470
                             " for method %s (the latter includes query and"
1471
                             " should therefore be given as well)" %
1472
                             (handler, method)))
1473

    
1474

    
1475
if __name__ == "__main__":
1476
  testutils.GanetiTestProgram()