Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ c1513c7f

History | View | Annotate | Download (21.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 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
"""Remote API QA tests.
23

24
"""
25

    
26
import tempfile
27
import random
28
import re
29
import itertools
30

    
31
from ganeti import utils
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import cli
35
from ganeti import rapi
36
from ganeti import objects
37
from ganeti import query
38
from ganeti import compat
39
from ganeti import qlang
40

    
41
import ganeti.rapi.client        # pylint: disable=W0611
42
import ganeti.rapi.client_utils
43

    
44
import qa_config
45
import qa_utils
46
import qa_error
47

    
48
from qa_utils import (AssertEqual, AssertIn, AssertMatch, StartLocalCommand)
49
from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG
50

    
51

    
52
_rapi_ca = None
53
_rapi_client = None
54
_rapi_username = None
55
_rapi_password = None
56

    
57

    
58
def Setup(username, password):
59
  """Configures the RAPI client.
60

61
  """
62
  # pylint: disable=W0603
63
  # due to global usage
64
  global _rapi_ca
65
  global _rapi_client
66
  global _rapi_username
67
  global _rapi_password
68

    
69
  _rapi_username = username
70
  _rapi_password = password
71

    
72
  master = qa_config.GetMasterNode()
73

    
74
  # Load RAPI certificate from master node
75
  cmd = ["cat", constants.RAPI_CERT_FILE]
76

    
77
  # Write to temporary file
78
  _rapi_ca = tempfile.NamedTemporaryFile()
79
  _rapi_ca.write(qa_utils.GetCommandOutput(master["primary"],
80
                                           utils.ShellQuoteArgs(cmd)))
81
  _rapi_ca.flush()
82

    
83
  port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
84
  cfg_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
85
                                           proxy="")
86

    
87
  _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
88
                                              username=username,
89
                                              password=password,
90
                                              curl_config_fn=cfg_curl)
91

    
92
  print "RAPI protocol version: %s" % _rapi_client.GetVersion()
93

    
94

    
95
INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
96
                   "admin_state",
97
                   "disk_template", "disk.sizes",
98
                   "nic.ips", "nic.macs", "nic.modes", "nic.links",
99
                   "beparams", "hvparams",
100
                   "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
101

    
102
NODE_FIELDS = ("name", "dtotal", "dfree",
103
               "mtotal", "mnode", "mfree",
104
               "pinst_cnt", "sinst_cnt", "tags")
105

    
106
GROUP_FIELDS = frozenset([
107
  "name", "uuid",
108
  "alloc_policy",
109
  "node_cnt", "node_list",
110
  ])
111

    
112
JOB_FIELDS = frozenset([
113
  "id", "ops", "status", "summary",
114
  "opstatus", "opresult", "oplog",
115
  "received_ts", "start_ts", "end_ts",
116
  ])
117

    
118
LIST_FIELDS = ("id", "uri")
119

    
120

    
121
def Enabled():
122
  """Return whether remote API tests should be run.
123

124
  """
125
  return qa_config.TestEnabled("rapi")
126

    
127

    
128
def _DoTests(uris):
129
  # pylint: disable=W0212
130
  # due to _SendRequest usage
131
  results = []
132

    
133
  for uri, verify, method, body in uris:
134
    assert uri.startswith("/")
135

    
136
    print "%s %s" % (method, uri)
137
    data = _rapi_client._SendRequest(method, uri, None, body)
138

    
139
    if verify is not None:
140
      if callable(verify):
141
        verify(data)
142
      else:
143
        AssertEqual(data, verify)
144

    
145
    results.append(data)
146

    
147
  return results
148

    
149

    
150
def _VerifyReturnsJob(data):
151
  AssertMatch(data, r"^\d+$")
152

    
153

    
154
def TestVersion():
155
  """Testing remote API version.
156

157
  """
158
  _DoTests([
159
    ("/version", constants.RAPI_VERSION, "GET", None),
160
    ])
161

    
162

    
163
def TestEmptyCluster():
164
  """Testing remote API on an empty cluster.
165

166
  """
167
  master = qa_config.GetMasterNode()
168
  master_full = qa_utils.ResolveNodeName(master)
169

    
170
  def _VerifyInfo(data):
171
    AssertIn("name", data)
172
    AssertIn("master", data)
173
    AssertEqual(data["master"], master_full)
174

    
175
  def _VerifyNodes(data):
176
    master_entry = {
177
      "id": master_full,
178
      "uri": "/2/nodes/%s" % master_full,
179
      }
180
    AssertIn(master_entry, data)
181

    
182
  def _VerifyNodesBulk(data):
183
    for node in data:
184
      for entry in NODE_FIELDS:
185
        AssertIn(entry, node)
186

    
187
  def _VerifyGroups(data):
188
    default_group = {
189
      "name": constants.INITIAL_NODE_GROUP_NAME,
190
      "uri": "/2/groups/" + constants.INITIAL_NODE_GROUP_NAME,
191
      }
192
    AssertIn(default_group, data)
193

    
194
  def _VerifyGroupsBulk(data):
195
    for group in data:
196
      for field in GROUP_FIELDS:
197
        AssertIn(field, group)
198

    
199
  _DoTests([
200
    ("/", None, "GET", None),
201
    ("/2/info", _VerifyInfo, "GET", None),
202
    ("/2/tags", None, "GET", None),
203
    ("/2/nodes", _VerifyNodes, "GET", None),
204
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
205
    ("/2/groups", _VerifyGroups, "GET", None),
206
    ("/2/groups?bulk=1", _VerifyGroupsBulk, "GET", None),
207
    ("/2/instances", [], "GET", None),
208
    ("/2/instances?bulk=1", [], "GET", None),
209
    ("/2/os", None, "GET", None),
210
    ])
211

    
212
  # Test HTTP Not Found
213
  for method in ["GET", "PUT", "POST", "DELETE"]:
214
    try:
215
      _DoTests([("/99/resource/not/here/99", None, method, None)])
216
    except rapi.client.GanetiApiError, err:
217
      AssertEqual(err.code, 404)
218
    else:
219
      raise qa_error.Error("Non-existent resource didn't return HTTP 404")
220

    
221
  # Test HTTP Not Implemented
222
  for method in ["PUT", "POST", "DELETE"]:
223
    try:
224
      _DoTests([("/version", None, method, None)])
225
    except rapi.client.GanetiApiError, err:
226
      AssertEqual(err.code, 501)
227
    else:
228
      raise qa_error.Error("Non-implemented method didn't fail")
229

    
230

    
231
def TestRapiQuery():
232
  """Testing resource queries via remote API.
233

234
  """
235
  master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
236
  rnd = random.Random(7818)
237

    
238
  for what in constants.QR_VIA_RAPI:
239
    if what == constants.QR_JOB:
240
      namefield = "id"
241
    elif what == constants.QR_EXPORT:
242
      namefield = "export"
243
    else:
244
      namefield = "name"
245

    
246
    all_fields = query.ALL_FIELDS[what].keys()
247
    rnd.shuffle(all_fields)
248

    
249
    # No fields, should return everything
250
    result = _rapi_client.QueryFields(what)
251
    qresult = objects.QueryFieldsResponse.FromDict(result)
252
    AssertEqual(len(qresult.fields), len(all_fields))
253

    
254
    # One field
255
    result = _rapi_client.QueryFields(what, fields=[namefield])
256
    qresult = objects.QueryFieldsResponse.FromDict(result)
257
    AssertEqual(len(qresult.fields), 1)
258

    
259
    # Specify all fields, order must be correct
260
    result = _rapi_client.QueryFields(what, fields=all_fields)
261
    qresult = objects.QueryFieldsResponse.FromDict(result)
262
    AssertEqual(len(qresult.fields), len(all_fields))
263
    AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
264

    
265
    # Unknown field
266
    result = _rapi_client.QueryFields(what, fields=["_unknown!"])
267
    qresult = objects.QueryFieldsResponse.FromDict(result)
268
    AssertEqual(len(qresult.fields), 1)
269
    AssertEqual(qresult.fields[0].name, "_unknown!")
270
    AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
271

    
272
    # Try once more, this time without the client
273
    _DoTests([
274
      ("/2/query/%s/fields" % what, None, "GET", None),
275
      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
276
       None, "GET", None),
277
      ])
278

    
279
    # Try missing query argument
280
    try:
281
      _DoTests([
282
        ("/2/query/%s" % what, None, "GET", None),
283
        ])
284
    except rapi.client.GanetiApiError, err:
285
      AssertEqual(err.code, 400)
286
    else:
287
      raise qa_error.Error("Request missing 'fields' parameter didn't fail")
288

    
289
    def _Check(exp_fields, data):
290
      qresult = objects.QueryResponse.FromDict(data)
291
      AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
292
      if not isinstance(qresult.data, list):
293
        raise qa_error.Error("Query did not return a list")
294

    
295
    _DoTests([
296
      # Specify fields in query
297
      ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
298
       compat.partial(_Check, all_fields), "GET", None),
299

    
300
      ("/2/query/%s?fields=%s" % (what, namefield),
301
       compat.partial(_Check, [namefield]), "GET", None),
302

    
303
      # Note the spaces
304
      ("/2/query/%s?fields=%s,%%20%s%%09,%s%%20" %
305
       (what, namefield, namefield, namefield),
306
       compat.partial(_Check, [namefield] * 3), "GET", None),
307

    
308
      # PUT with fields in query
309
      ("/2/query/%s?fields=%s" % (what, namefield),
310
       compat.partial(_Check, [namefield]), "PUT", {}),
311

    
312
      # Fields in body
313
      ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
314
         "fields": all_fields,
315
         }),
316

    
317
      ("/2/query/%s" % what, compat.partial(_Check, [namefield] * 4), "PUT", {
318
         "fields": [namefield] * 4,
319
         }),
320
      ])
321

    
322
    def _CheckFilter():
323
      _DoTests([
324
        # With filter
325
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
326
           "fields": all_fields,
327
           "filter": [qlang.OP_TRUE, namefield],
328
           }),
329
        ])
330

    
331
    if what == constants.QR_LOCK:
332
      # Locks can't be filtered
333
      try:
334
        _CheckFilter()
335
      except rapi.client.GanetiApiError, err:
336
        AssertEqual(err.code, 500)
337
      else:
338
        raise qa_error.Error("Filtering locks didn't fail")
339
    else:
340
      _CheckFilter()
341

    
342
    if what == constants.QR_NODE:
343
      # Test with filter
344
      (nodes, ) = _DoTests([("/2/query/%s" % what,
345
        compat.partial(_Check, ["name", "master"]), "PUT", {
346
        "fields": ["name", "master"],
347
        "filter": [qlang.OP_TRUE, "master"],
348
        })])
349
      qresult = objects.QueryResponse.FromDict(nodes)
350
      AssertEqual(qresult.data, [
351
        [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
352
        ])
353

    
354

    
355
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
356
def TestInstance(instance):
357
  """Testing getting instance(s) info via remote API.
358

359
  """
360
  def _VerifyInstance(data):
361
    for entry in INSTANCE_FIELDS:
362
      AssertIn(entry, data)
363

    
364
  def _VerifyInstancesList(data):
365
    for instance in data:
366
      for entry in LIST_FIELDS:
367
        AssertIn(entry, instance)
368

    
369
  def _VerifyInstancesBulk(data):
370
    for instance_data in data:
371
      _VerifyInstance(instance_data)
372

    
373
  _DoTests([
374
    ("/2/instances/%s" % instance["name"], _VerifyInstance, "GET", None),
375
    ("/2/instances", _VerifyInstancesList, "GET", None),
376
    ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
377
    ("/2/instances/%s/activate-disks" % instance["name"],
378
     _VerifyReturnsJob, "PUT", None),
379
    ("/2/instances/%s/deactivate-disks" % instance["name"],
380
     _VerifyReturnsJob, "PUT", None),
381
    ])
382

    
383
  # Test OpBackupPrepare
384
  (job_id, ) = _DoTests([
385
    ("/2/instances/%s/prepare-export?mode=%s" %
386
     (instance["name"], constants.EXPORT_MODE_REMOTE),
387
     _VerifyReturnsJob, "PUT", None),
388
    ])
389

    
390
  result = _WaitForRapiJob(job_id)[0]
391
  AssertEqual(len(result["handshake"]), 3)
392
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
393
  AssertEqual(len(result["x509_key_name"]), 3)
394
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
395

    
396

    
397
def TestNode(node):
398
  """Testing getting node(s) info via remote API.
399

400
  """
401
  def _VerifyNode(data):
402
    for entry in NODE_FIELDS:
403
      AssertIn(entry, data)
404

    
405
  def _VerifyNodesList(data):
406
    for node in data:
407
      for entry in LIST_FIELDS:
408
        AssertIn(entry, node)
409

    
410
  def _VerifyNodesBulk(data):
411
    for node_data in data:
412
      _VerifyNode(node_data)
413

    
414
  _DoTests([
415
    ("/2/nodes/%s" % node["primary"], _VerifyNode, "GET", None),
416
    ("/2/nodes", _VerifyNodesList, "GET", None),
417
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
418
    ])
419

    
420

    
421
def _FilterTags(seq):
422
  """Removes unwanted tags from a sequence.
423

424
  """
425
  ignore_re = qa_config.get("ignore-tags-re", None)
426

    
427
  if ignore_re:
428
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
429
  else:
430
    return seq
431

    
432

    
433
def TestTags(kind, name, tags):
434
  """Tests .../tags resources.
435

436
  """
437
  if kind == constants.TAG_CLUSTER:
438
    uri = "/2/tags"
439
  elif kind == constants.TAG_NODE:
440
    uri = "/2/nodes/%s/tags" % name
441
  elif kind == constants.TAG_INSTANCE:
442
    uri = "/2/instances/%s/tags" % name
443
  elif kind == constants.TAG_NODEGROUP:
444
    uri = "/2/groups/%s/tags" % name
445
  else:
446
    raise errors.ProgrammerError("Unknown tag kind")
447

    
448
  def _VerifyTags(data):
449
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
450

    
451
  queryargs = "&".join("tag=%s" % i for i in tags)
452

    
453
  # Add tags
454
  (job_id, ) = _DoTests([
455
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
456
    ])
457
  _WaitForRapiJob(job_id)
458

    
459
  # Retrieve tags
460
  _DoTests([
461
    (uri, _VerifyTags, "GET", None),
462
    ])
463

    
464
  # Remove tags
465
  (job_id, ) = _DoTests([
466
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "DELETE", None),
467
    ])
468
  _WaitForRapiJob(job_id)
469

    
470

    
471
def _WaitForRapiJob(job_id):
472
  """Waits for a job to finish.
473

474
  """
475
  def _VerifyJob(data):
476
    AssertEqual(data["id"], job_id)
477
    for field in JOB_FIELDS:
478
      AssertIn(field, data)
479

    
480
  _DoTests([
481
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
482
    ])
483

    
484
  return rapi.client_utils.PollJob(_rapi_client, job_id,
485
                                   cli.StdioJobPollReportCb())
486

    
487

    
488
def TestRapiNodeGroups():
489
  """Test several node group operations using RAPI.
490

491
  """
492
  groups = qa_config.get("groups", {})
493
  group1, group2, group3 = groups.get("inexistent-groups",
494
                                      ["group1", "group2", "group3"])[:3]
495

    
496
  # Create a group with no attributes
497
  body = {
498
    "name": group1,
499
    }
500

    
501
  (job_id, ) = _DoTests([
502
    ("/2/groups", _VerifyReturnsJob, "POST", body),
503
    ])
504

    
505
  _WaitForRapiJob(job_id)
506

    
507
  # Create a group specifying alloc_policy
508
  body = {
509
    "name": group2,
510
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
511
    }
512

    
513
  (job_id, ) = _DoTests([
514
    ("/2/groups", _VerifyReturnsJob, "POST", body),
515
    ])
516

    
517
  _WaitForRapiJob(job_id)
518

    
519
  # Modify alloc_policy
520
  body = {
521
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
522
    }
523

    
524
  (job_id, ) = _DoTests([
525
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
526
    ])
527

    
528
  _WaitForRapiJob(job_id)
529

    
530
  # Rename a group
531
  body = {
532
    "new_name": group3,
533
    }
534

    
535
  (job_id, ) = _DoTests([
536
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
537
    ])
538

    
539
  _WaitForRapiJob(job_id)
540

    
541
  # Delete groups
542
  for group in [group1, group3]:
543
    (job_id, ) = _DoTests([
544
      ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
545
      ])
546

    
547
    _WaitForRapiJob(job_id)
548

    
549

    
550
def TestRapiInstanceAdd(node, use_client):
551
  """Test adding a new instance via RAPI"""
552
  instance = qa_config.AcquireInstance()
553
  try:
554
    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
555
    disks = [{"size": size} for size in disk_sizes]
556
    nics = [{}]
557

    
558
    beparams = {
559
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
560
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
561
      }
562

    
563
    if use_client:
564
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
565
                                           instance["name"],
566
                                           constants.DT_PLAIN,
567
                                           disks, nics,
568
                                           os=qa_config.get("os"),
569
                                           pnode=node["primary"],
570
                                           beparams=beparams)
571
    else:
572
      body = {
573
        "__version__": 1,
574
        "mode": constants.INSTANCE_CREATE,
575
        "name": instance["name"],
576
        "os_type": qa_config.get("os"),
577
        "disk_template": constants.DT_PLAIN,
578
        "pnode": node["primary"],
579
        "beparams": beparams,
580
        "disks": disks,
581
        "nics": nics,
582
        }
583

    
584
      (job_id, ) = _DoTests([
585
        ("/2/instances", _VerifyReturnsJob, "POST", body),
586
        ])
587

    
588
    _WaitForRapiJob(job_id)
589

    
590
    return instance
591
  except:
592
    qa_config.ReleaseInstance(instance)
593
    raise
594

    
595

    
596
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
597
def TestRapiInstanceRemove(instance, use_client):
598
  """Test removing instance via RAPI"""
599
  if use_client:
600
    job_id = _rapi_client.DeleteInstance(instance["name"])
601
  else:
602
    (job_id, ) = _DoTests([
603
      ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
604
      ])
605

    
606
  _WaitForRapiJob(job_id)
607

    
608
  qa_config.ReleaseInstance(instance)
609

    
610

    
611
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
612
def TestRapiInstanceMigrate(instance):
613
  """Test migrating instance via RAPI"""
614
  # Move to secondary node
615
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
616
  qa_utils.RunInstanceCheck(instance, True)
617
  # And back to previous primary
618
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
619

    
620

    
621
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
622
def TestRapiInstanceFailover(instance):
623
  """Test failing over instance via RAPI"""
624
  # Move to secondary node
625
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
626
  qa_utils.RunInstanceCheck(instance, True)
627
  # And back to previous primary
628
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
629

    
630

    
631
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
632
def TestRapiInstanceShutdown(instance):
633
  """Test stopping an instance via RAPI"""
634
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
635

    
636

    
637
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
638
def TestRapiInstanceStartup(instance):
639
  """Test starting an instance via RAPI"""
640
  _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
641

    
642

    
643
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
644
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
645
  """Test renaming instance via RAPI
646

647
  This must leave the instance with the original name (in the
648
  non-failure case).
649

650
  """
651
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
652
  qa_utils.RunInstanceCheck(rename_source, False)
653
  qa_utils.RunInstanceCheck(rename_target, False)
654
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
655
  qa_utils.RunInstanceCheck(rename_target, False)
656

    
657

    
658
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
659
def TestRapiInstanceReinstall(instance):
660
  """Test reinstalling an instance via RAPI"""
661
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
662
  # By default, the instance is started again
663
  qa_utils.RunInstanceCheck(instance, True)
664

    
665
  # Reinstall again without starting
666
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"],
667
                                                 no_startup=True))
668

    
669

    
670
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
671
def TestRapiInstanceReplaceDisks(instance):
672
  """Test replacing instance disks via RAPI"""
673
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
674
    mode=constants.REPLACE_DISK_AUTO, disks=[]))
675
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
676
    mode=constants.REPLACE_DISK_SEC, disks="0"))
677

    
678

    
679
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
680
def TestRapiInstanceModify(instance):
681
  """Test modifying instance via RAPI"""
682
  def _ModifyInstance(**kwargs):
683
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
684

    
685
  _ModifyInstance(hvparams={
686
    constants.HV_KERNEL_ARGS: "single",
687
    })
688

    
689
  _ModifyInstance(beparams={
690
    constants.BE_VCPUS: 3,
691
    })
692

    
693
  _ModifyInstance(beparams={
694
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
695
    })
696

    
697
  _ModifyInstance(hvparams={
698
    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
699
    })
700

    
701

    
702
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
703
def TestRapiInstanceConsole(instance):
704
  """Test getting instance console information via RAPI"""
705
  result = _rapi_client.GetInstanceConsole(instance["name"])
706
  console = objects.InstanceConsole.FromDict(result)
707
  AssertEqual(console.Validate(), True)
708
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
709

    
710

    
711
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
712
def TestRapiStoppedInstanceConsole(instance):
713
  """Test getting stopped instance's console information via RAPI"""
714
  try:
715
    _rapi_client.GetInstanceConsole(instance["name"])
716
  except rapi.client.GanetiApiError, err:
717
    AssertEqual(err.code, 503)
718
  else:
719
    raise qa_error.Error("Getting console for stopped instance didn't"
720
                         " return HTTP 503")
721

    
722

    
723
def GetOperatingSystems():
724
  """Retrieves a list of all available operating systems.
725

726
  """
727
  return _rapi_client.GetOperatingSystems()
728

    
729

    
730
def TestInterClusterInstanceMove(src_instance, dest_instance,
731
                                 pnode, snode, tnode):
732
  """Test tools/move-instance"""
733
  master = qa_config.GetMasterNode()
734

    
735
  rapi_pw_file = tempfile.NamedTemporaryFile()
736
  rapi_pw_file.write(_rapi_password)
737
  rapi_pw_file.flush()
738

    
739
  # TODO: Run some instance tests before moving back
740

    
741
  if snode is None:
742
    # instance is not redundant, but we still need to pass a node
743
    # (which will be ignored)
744
    fsec = tnode
745
  else:
746
    fsec = snode
747
  # note: pnode:snode are the *current* nodes, so we move it first to
748
  # tnode:pnode, then back to pnode:snode
749
  for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
750
                          tnode["primary"], pnode["primary"]),
751
                         (dest_instance["name"], src_instance["name"],
752
                          pnode["primary"], fsec["primary"])]:
753
    cmd = [
754
      "../tools/move-instance",
755
      "--verbose",
756
      "--src-ca-file=%s" % _rapi_ca.name,
757
      "--src-username=%s" % _rapi_username,
758
      "--src-password-file=%s" % rapi_pw_file.name,
759
      "--dest-instance-name=%s" % di,
760
      "--dest-primary-node=%s" % pn,
761
      "--dest-secondary-node=%s" % sn,
762
      "--net=0:mac=%s" % constants.VALUE_GENERATE,
763
      master["primary"],
764
      master["primary"],
765
      si,
766
      ]
767

    
768
    qa_utils.RunInstanceCheck(di, False)
769
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
770
    qa_utils.RunInstanceCheck(si, False)
771
    qa_utils.RunInstanceCheck(di, True)