Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 090128b6

History | View | Annotate | Download (23.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008, 2009, 2010, 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
"""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
from ganeti import pathutils
41

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

    
45
import qa_config
46
import qa_utils
47
import qa_error
48

    
49
from qa_instance import IsFailoverSupported
50
from qa_instance import IsMigrationSupported
51
from qa_instance import IsDiskReplacingSupported
52
from qa_utils import (AssertEqual, AssertIn, AssertMatch, StartLocalCommand)
53
from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG
54

    
55

    
56
_rapi_ca = None
57
_rapi_client = None
58
_rapi_username = None
59
_rapi_password = None
60

    
61

    
62
def Setup(username, password):
63
  """Configures the RAPI client.
64

65
  """
66
  # pylint: disable=W0603
67
  # due to global usage
68
  global _rapi_ca
69
  global _rapi_client
70
  global _rapi_username
71
  global _rapi_password
72

    
73
  _rapi_username = username
74
  _rapi_password = password
75

    
76
  master = qa_config.GetMasterNode()
77

    
78
  # Load RAPI certificate from master node
79
  cmd = ["cat", qa_utils.MakeNodePath(master, pathutils.RAPI_CERT_FILE)]
80

    
81
  # Write to temporary file
82
  _rapi_ca = tempfile.NamedTemporaryFile()
83
  _rapi_ca.write(qa_utils.GetCommandOutput(master.primary,
84
                                           utils.ShellQuoteArgs(cmd)))
85
  _rapi_ca.flush()
86

    
87
  port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
88
  cfg_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
89
                                           proxy="")
90

    
91
  if qa_config.UseVirtualCluster():
92
    # TODO: Implement full support for RAPI on virtual clusters
93
    print qa_utils.FormatWarning("RAPI tests are not yet supported on"
94
                                 " virtual clusters and will be disabled")
95

    
96
    assert _rapi_client is None
97
  else:
98
    _rapi_client = rapi.client.GanetiRapiClient(master.primary, port=port,
99
                                                username=username,
100
                                                password=password,
101
                                                curl_config_fn=cfg_curl)
102

    
103
    print "RAPI protocol version: %s" % _rapi_client.GetVersion()
104

    
105

    
106
INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
107
                   "admin_state",
108
                   "disk_template", "disk.sizes",
109
                   "nic.ips", "nic.macs", "nic.modes", "nic.links",
110
                   "beparams", "hvparams",
111
                   "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
112

    
113
NODE_FIELDS = ("name", "dtotal", "dfree",
114
               "mtotal", "mnode", "mfree",
115
               "pinst_cnt", "sinst_cnt", "tags")
116

    
117
GROUP_FIELDS = compat.UniqueFrozenset([
118
  "name", "uuid",
119
  "alloc_policy",
120
  "node_cnt", "node_list",
121
  ])
122

    
123
JOB_FIELDS = compat.UniqueFrozenset([
124
  "id", "ops", "status", "summary",
125
  "opstatus", "opresult", "oplog",
126
  "received_ts", "start_ts", "end_ts",
127
  ])
128

    
129
LIST_FIELDS = ("id", "uri")
130

    
131

    
132
def Enabled():
133
  """Return whether remote API tests should be run.
134

135
  """
136
  # TODO: Implement RAPI tests for virtual clusters
137
  return (qa_config.TestEnabled("rapi") and
138
          not qa_config.UseVirtualCluster())
139

    
140

    
141
def _DoTests(uris):
142
  # pylint: disable=W0212
143
  # due to _SendRequest usage
144
  results = []
145

    
146
  for uri, verify, method, body in uris:
147
    assert uri.startswith("/")
148

    
149
    print "%s %s" % (method, uri)
150
    data = _rapi_client._SendRequest(method, uri, None, body)
151

    
152
    if verify is not None:
153
      if callable(verify):
154
        verify(data)
155
      else:
156
        AssertEqual(data, verify)
157

    
158
    results.append(data)
159

    
160
  return results
161

    
162

    
163
def _VerifyReturnsJob(data):
164
  if not isinstance(data, int):
165
    AssertMatch(data, r"^\d+$")
166

    
167

    
168
def TestVersion():
169
  """Testing remote API version.
170

171
  """
172
  _DoTests([
173
    ("/version", constants.RAPI_VERSION, "GET", None),
174
    ])
175

    
176

    
177
def TestEmptyCluster():
178
  """Testing remote API on an empty cluster.
179

180
  """
181
  master = qa_config.GetMasterNode()
182
  master_full = qa_utils.ResolveNodeName(master)
183

    
184
  def _VerifyInfo(data):
185
    AssertIn("name", data)
186
    AssertIn("master", data)
187
    AssertEqual(data["master"], master_full)
188

    
189
  def _VerifyNodes(data):
190
    master_entry = {
191
      "id": master_full,
192
      "uri": "/2/nodes/%s" % master_full,
193
      }
194
    AssertIn(master_entry, data)
195

    
196
  def _VerifyNodesBulk(data):
197
    for node in data:
198
      for entry in NODE_FIELDS:
199
        AssertIn(entry, node)
200

    
201
  def _VerifyGroups(data):
202
    default_group = {
203
      "name": constants.INITIAL_NODE_GROUP_NAME,
204
      "uri": "/2/groups/" + constants.INITIAL_NODE_GROUP_NAME,
205
      }
206
    AssertIn(default_group, data)
207

    
208
  def _VerifyGroupsBulk(data):
209
    for group in data:
210
      for field in GROUP_FIELDS:
211
        AssertIn(field, group)
212

    
213
  _DoTests([
214
    ("/", None, "GET", None),
215
    ("/2/info", _VerifyInfo, "GET", None),
216
    ("/2/tags", None, "GET", None),
217
    ("/2/nodes", _VerifyNodes, "GET", None),
218
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
219
    ("/2/groups", _VerifyGroups, "GET", None),
220
    ("/2/groups?bulk=1", _VerifyGroupsBulk, "GET", None),
221
    ("/2/instances", [], "GET", None),
222
    ("/2/instances?bulk=1", [], "GET", None),
223
    ("/2/os", None, "GET", None),
224
    ])
225

    
226
  # Test HTTP Not Found
227
  for method in ["GET", "PUT", "POST", "DELETE"]:
228
    try:
229
      _DoTests([("/99/resource/not/here/99", None, method, None)])
230
    except rapi.client.GanetiApiError, err:
231
      AssertEqual(err.code, 404)
232
    else:
233
      raise qa_error.Error("Non-existent resource didn't return HTTP 404")
234

    
235
  # Test HTTP Not Implemented
236
  for method in ["PUT", "POST", "DELETE"]:
237
    try:
238
      _DoTests([("/version", None, method, None)])
239
    except rapi.client.GanetiApiError, err:
240
      AssertEqual(err.code, 501)
241
    else:
242
      raise qa_error.Error("Non-implemented method didn't fail")
243

    
244

    
245
def TestRapiQuery():
246
  """Testing resource queries via remote API.
247

248
  """
249
  master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
250
  rnd = random.Random(7818)
251

    
252
  for what in constants.QR_VIA_RAPI:
253
    if what == constants.QR_JOB:
254
      namefield = "id"
255
    elif what == constants.QR_EXPORT:
256
      namefield = "export"
257
    else:
258
      namefield = "name"
259

    
260
    all_fields = query.ALL_FIELDS[what].keys()
261
    rnd.shuffle(all_fields)
262

    
263
    # No fields, should return everything
264
    result = _rapi_client.QueryFields(what)
265
    qresult = objects.QueryFieldsResponse.FromDict(result)
266
    AssertEqual(len(qresult.fields), len(all_fields))
267

    
268
    # One field
269
    result = _rapi_client.QueryFields(what, fields=[namefield])
270
    qresult = objects.QueryFieldsResponse.FromDict(result)
271
    AssertEqual(len(qresult.fields), 1)
272

    
273
    # Specify all fields, order must be correct
274
    result = _rapi_client.QueryFields(what, fields=all_fields)
275
    qresult = objects.QueryFieldsResponse.FromDict(result)
276
    AssertEqual(len(qresult.fields), len(all_fields))
277
    AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
278

    
279
    # Unknown field
280
    result = _rapi_client.QueryFields(what, fields=["_unknown!"])
281
    qresult = objects.QueryFieldsResponse.FromDict(result)
282
    AssertEqual(len(qresult.fields), 1)
283
    AssertEqual(qresult.fields[0].name, "_unknown!")
284
    AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
285

    
286
    # Try once more, this time without the client
287
    _DoTests([
288
      ("/2/query/%s/fields" % what, None, "GET", None),
289
      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
290
       None, "GET", None),
291
      ])
292

    
293
    # Try missing query argument
294
    try:
295
      _DoTests([
296
        ("/2/query/%s" % what, None, "GET", None),
297
        ])
298
    except rapi.client.GanetiApiError, err:
299
      AssertEqual(err.code, 400)
300
    else:
301
      raise qa_error.Error("Request missing 'fields' parameter didn't fail")
302

    
303
    def _Check(exp_fields, data):
304
      qresult = objects.QueryResponse.FromDict(data)
305
      AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
306
      if not isinstance(qresult.data, list):
307
        raise qa_error.Error("Query did not return a list")
308

    
309
    _DoTests([
310
      # Specify fields in query
311
      ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
312
       compat.partial(_Check, all_fields), "GET", None),
313

    
314
      ("/2/query/%s?fields=%s" % (what, namefield),
315
       compat.partial(_Check, [namefield]), "GET", None),
316

    
317
      # Note the spaces
318
      ("/2/query/%s?fields=%s,%%20%s%%09,%s%%20" %
319
       (what, namefield, namefield, namefield),
320
       compat.partial(_Check, [namefield] * 3), "GET", None),
321

    
322
      # PUT with fields in query
323
      ("/2/query/%s?fields=%s" % (what, namefield),
324
       compat.partial(_Check, [namefield]), "PUT", {}),
325

    
326
      # Fields in body
327
      ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
328
         "fields": all_fields,
329
         }),
330

    
331
      ("/2/query/%s" % what, compat.partial(_Check, [namefield] * 4), "PUT", {
332
         "fields": [namefield] * 4,
333
         }),
334
      ])
335

    
336
    def _CheckFilter():
337
      _DoTests([
338
        # With filter
339
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
340
           "fields": all_fields,
341
           "filter": [qlang.OP_TRUE, namefield],
342
           }),
343
        ])
344

    
345
    if what == constants.QR_LOCK:
346
      # Locks can't be filtered
347
      try:
348
        _CheckFilter()
349
      except rapi.client.GanetiApiError, err:
350
        AssertEqual(err.code, 500)
351
      else:
352
        raise qa_error.Error("Filtering locks didn't fail")
353
    else:
354
      _CheckFilter()
355

    
356
    if what == constants.QR_NODE:
357
      # Test with filter
358
      (nodes, ) = _DoTests(
359
        [("/2/query/%s" % what,
360
          compat.partial(_Check, ["name", "master"]), "PUT",
361
          {"fields": ["name", "master"],
362
           "filter": [qlang.OP_TRUE, "master"],
363
           })])
364
      qresult = objects.QueryResponse.FromDict(nodes)
365
      AssertEqual(qresult.data, [
366
        [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
367
        ])
368

    
369

    
370
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
371
def TestInstance(instance):
372
  """Testing getting instance(s) info via remote API.
373

374
  """
375
  def _VerifyInstance(data):
376
    for entry in INSTANCE_FIELDS:
377
      AssertIn(entry, data)
378

    
379
  def _VerifyInstancesList(data):
380
    for instance in data:
381
      for entry in LIST_FIELDS:
382
        AssertIn(entry, instance)
383

    
384
  def _VerifyInstancesBulk(data):
385
    for instance_data in data:
386
      _VerifyInstance(instance_data)
387

    
388
  _DoTests([
389
    ("/2/instances/%s" % instance.name, _VerifyInstance, "GET", None),
390
    ("/2/instances", _VerifyInstancesList, "GET", None),
391
    ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
392
    ("/2/instances/%s/activate-disks" % instance.name,
393
     _VerifyReturnsJob, "PUT", None),
394
    ("/2/instances/%s/deactivate-disks" % instance.name,
395
     _VerifyReturnsJob, "PUT", None),
396
    ])
397

    
398
  # Test OpBackupPrepare
399
  (job_id, ) = _DoTests([
400
    ("/2/instances/%s/prepare-export?mode=%s" %
401
     (instance.name, constants.EXPORT_MODE_REMOTE),
402
     _VerifyReturnsJob, "PUT", None),
403
    ])
404

    
405
  result = _WaitForRapiJob(job_id)[0]
406
  AssertEqual(len(result["handshake"]), 3)
407
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
408
  AssertEqual(len(result["x509_key_name"]), 3)
409
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
410

    
411

    
412
def TestNode(node):
413
  """Testing getting node(s) info via remote API.
414

415
  """
416
  def _VerifyNode(data):
417
    for entry in NODE_FIELDS:
418
      AssertIn(entry, data)
419

    
420
  def _VerifyNodesList(data):
421
    for node in data:
422
      for entry in LIST_FIELDS:
423
        AssertIn(entry, node)
424

    
425
  def _VerifyNodesBulk(data):
426
    for node_data in data:
427
      _VerifyNode(node_data)
428

    
429
  _DoTests([
430
    ("/2/nodes/%s" % node.primary, _VerifyNode, "GET", None),
431
    ("/2/nodes", _VerifyNodesList, "GET", None),
432
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
433
    ])
434

    
435

    
436
def _FilterTags(seq):
437
  """Removes unwanted tags from a sequence.
438

439
  """
440
  ignore_re = qa_config.get("ignore-tags-re", None)
441

    
442
  if ignore_re:
443
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
444
  else:
445
    return seq
446

    
447

    
448
def TestTags(kind, name, tags):
449
  """Tests .../tags resources.
450

451
  """
452
  if kind == constants.TAG_CLUSTER:
453
    uri = "/2/tags"
454
  elif kind == constants.TAG_NODE:
455
    uri = "/2/nodes/%s/tags" % name
456
  elif kind == constants.TAG_INSTANCE:
457
    uri = "/2/instances/%s/tags" % name
458
  elif kind == constants.TAG_NODEGROUP:
459
    uri = "/2/groups/%s/tags" % name
460
  else:
461
    raise errors.ProgrammerError("Unknown tag kind")
462

    
463
  def _VerifyTags(data):
464
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
465

    
466
  queryargs = "&".join("tag=%s" % i for i in tags)
467

    
468
  # Add tags
469
  (job_id, ) = _DoTests([
470
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
471
    ])
472
  _WaitForRapiJob(job_id)
473

    
474
  # Retrieve tags
475
  _DoTests([
476
    (uri, _VerifyTags, "GET", None),
477
    ])
478

    
479
  # Remove tags
480
  (job_id, ) = _DoTests([
481
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "DELETE", None),
482
    ])
483
  _WaitForRapiJob(job_id)
484

    
485

    
486
def _WaitForRapiJob(job_id):
487
  """Waits for a job to finish.
488

489
  """
490
  def _VerifyJob(data):
491
    AssertEqual(data["id"], job_id)
492
    for field in JOB_FIELDS:
493
      AssertIn(field, data)
494

    
495
  _DoTests([
496
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
497
    ])
498

    
499
  return rapi.client_utils.PollJob(_rapi_client, job_id,
500
                                   cli.StdioJobPollReportCb())
501

    
502

    
503
def TestRapiNodeGroups():
504
  """Test several node group operations using RAPI.
505

506
  """
507
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
508

    
509
  # Create a group with no attributes
510
  body = {
511
    "name": group1,
512
    }
513

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

    
518
  _WaitForRapiJob(job_id)
519

    
520
  # Create a group specifying alloc_policy
521
  body = {
522
    "name": group2,
523
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
524
    }
525

    
526
  (job_id, ) = _DoTests([
527
    ("/2/groups", _VerifyReturnsJob, "POST", body),
528
    ])
529

    
530
  _WaitForRapiJob(job_id)
531

    
532
  # Modify alloc_policy
533
  body = {
534
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
535
    }
536

    
537
  (job_id, ) = _DoTests([
538
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
539
    ])
540

    
541
  _WaitForRapiJob(job_id)
542

    
543
  # Rename a group
544
  body = {
545
    "new_name": group3,
546
    }
547

    
548
  (job_id, ) = _DoTests([
549
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
550
    ])
551

    
552
  _WaitForRapiJob(job_id)
553

    
554
  # Delete groups
555
  for group in [group1, group3]:
556
    (job_id, ) = _DoTests([
557
      ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
558
      ])
559

    
560
    _WaitForRapiJob(job_id)
561

    
562

    
563
def TestRapiInstanceAdd(node, use_client):
564
  """Test adding a new instance via RAPI"""
565
  instance = qa_config.AcquireInstance()
566
  instance.SetDiskTemplate(constants.DT_PLAIN)
567
  try:
568
    disks = [{"size": utils.ParseUnit(d.get("size")),
569
              "name": str(d.get("name"))}
570
             for d in qa_config.GetDiskOptions()]
571
    nic0_mac = instance.GetNicMacAddr(0, constants.VALUE_GENERATE)
572
    nics = [{
573
      constants.INIC_MAC: nic0_mac,
574
      }]
575

    
576
    beparams = {
577
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
578
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
579
      }
580

    
581
    if use_client:
582
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
583
                                           instance.name,
584
                                           constants.DT_PLAIN,
585
                                           disks, nics,
586
                                           os=qa_config.get("os"),
587
                                           pnode=node.primary,
588
                                           beparams=beparams)
589
    else:
590
      body = {
591
        "__version__": 1,
592
        "mode": constants.INSTANCE_CREATE,
593
        "name": instance.name,
594
        "os_type": qa_config.get("os"),
595
        "disk_template": constants.DT_PLAIN,
596
        "pnode": node.primary,
597
        "beparams": beparams,
598
        "disks": disks,
599
        "nics": nics,
600
        }
601

    
602
      (job_id, ) = _DoTests([
603
        ("/2/instances", _VerifyReturnsJob, "POST", body),
604
        ])
605

    
606
    _WaitForRapiJob(job_id)
607

    
608
    return instance
609
  except:
610
    instance.Release()
611
    raise
612

    
613

    
614
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
615
def TestRapiInstanceRemove(instance, use_client):
616
  """Test removing instance via RAPI"""
617
  if use_client:
618
    job_id = _rapi_client.DeleteInstance(instance.name)
619
  else:
620
    (job_id, ) = _DoTests([
621
      ("/2/instances/%s" % instance.name, _VerifyReturnsJob, "DELETE", None),
622
      ])
623

    
624
  _WaitForRapiJob(job_id)
625

    
626

    
627
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
628
def TestRapiInstanceMigrate(instance):
629
  """Test migrating instance via RAPI"""
630
  if not IsMigrationSupported(instance):
631
    print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
632
                              " test")
633
    return
634
  # Move to secondary node
635
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance.name))
636
  qa_utils.RunInstanceCheck(instance, True)
637
  # And back to previous primary
638
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance.name))
639

    
640

    
641
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
642
def TestRapiInstanceFailover(instance):
643
  """Test failing over instance via RAPI"""
644
  if not IsFailoverSupported(instance):
645
    print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
646
                              " test")
647
    return
648
  # Move to secondary node
649
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance.name))
650
  qa_utils.RunInstanceCheck(instance, True)
651
  # And back to previous primary
652
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance.name))
653

    
654

    
655
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
656
def TestRapiInstanceShutdown(instance):
657
  """Test stopping an instance via RAPI"""
658
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance.name))
659

    
660

    
661
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
662
def TestRapiInstanceStartup(instance):
663
  """Test starting an instance via RAPI"""
664
  _WaitForRapiJob(_rapi_client.StartupInstance(instance.name))
665

    
666

    
667
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
668
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
669
  """Test renaming instance via RAPI
670

671
  This must leave the instance with the original name (in the
672
  non-failure case).
673

674
  """
675
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
676
  qa_utils.RunInstanceCheck(rename_source, False)
677
  qa_utils.RunInstanceCheck(rename_target, False)
678
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
679
  qa_utils.RunInstanceCheck(rename_target, False)
680

    
681

    
682
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
683
def TestRapiInstanceReinstall(instance):
684
  """Test reinstalling an instance via RAPI"""
685
  if instance.disk_template == constants.DT_DISKLESS:
686
    print qa_utils.FormatInfo("Test not supported for diskless instances")
687
    return
688

    
689
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name))
690
  # By default, the instance is started again
691
  qa_utils.RunInstanceCheck(instance, True)
692

    
693
  # Reinstall again without starting
694
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name,
695
                                                 no_startup=True))
696

    
697

    
698
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
699
def TestRapiInstanceReplaceDisks(instance):
700
  """Test replacing instance disks via RAPI"""
701
  if not IsDiskReplacingSupported(instance):
702
    print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
703
                              " skipping test")
704
    return
705
  fn = _rapi_client.ReplaceInstanceDisks
706
  _WaitForRapiJob(fn(instance.name,
707
                     mode=constants.REPLACE_DISK_AUTO, disks=[]))
708
  _WaitForRapiJob(fn(instance.name,
709
                     mode=constants.REPLACE_DISK_SEC, disks="0"))
710

    
711

    
712
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
713
def TestRapiInstanceModify(instance):
714
  """Test modifying instance via RAPI"""
715
  default_hv = qa_config.GetDefaultHypervisor()
716

    
717
  def _ModifyInstance(**kwargs):
718
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance.name, **kwargs))
719

    
720
  _ModifyInstance(beparams={
721
    constants.BE_VCPUS: 3,
722
    })
723

    
724
  _ModifyInstance(beparams={
725
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
726
    })
727

    
728
  if default_hv == constants.HT_XEN_PVM:
729
    _ModifyInstance(hvparams={
730
      constants.HV_KERNEL_ARGS: "single",
731
      })
732
    _ModifyInstance(hvparams={
733
      constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
734
      })
735
  elif default_hv == constants.HT_XEN_HVM:
736
    _ModifyInstance(hvparams={
737
      constants.HV_BOOT_ORDER: "acn",
738
      })
739
    _ModifyInstance(hvparams={
740
      constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
741
      })
742

    
743

    
744
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
745
def TestRapiInstanceConsole(instance):
746
  """Test getting instance console information via RAPI"""
747
  result = _rapi_client.GetInstanceConsole(instance.name)
748
  console = objects.InstanceConsole.FromDict(result)
749
  AssertEqual(console.Validate(), True)
750
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance.name))
751

    
752

    
753
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
754
def TestRapiStoppedInstanceConsole(instance):
755
  """Test getting stopped instance's console information via RAPI"""
756
  try:
757
    _rapi_client.GetInstanceConsole(instance.name)
758
  except rapi.client.GanetiApiError, err:
759
    AssertEqual(err.code, 503)
760
  else:
761
    raise qa_error.Error("Getting console for stopped instance didn't"
762
                         " return HTTP 503")
763

    
764

    
765
def GetOperatingSystems():
766
  """Retrieves a list of all available operating systems.
767

768
  """
769
  return _rapi_client.GetOperatingSystems()
770

    
771

    
772
def TestInterClusterInstanceMove(src_instance, dest_instance,
773
                                 inodes, tnode):
774
  """Test tools/move-instance"""
775
  master = qa_config.GetMasterNode()
776

    
777
  rapi_pw_file = tempfile.NamedTemporaryFile()
778
  rapi_pw_file.write(_rapi_password)
779
  rapi_pw_file.flush()
780

    
781
  dest_instance.SetDiskTemplate(src_instance.disk_template)
782

    
783
  # TODO: Run some instance tests before moving back
784

    
785
  if len(inodes) > 1:
786
    # No disk template currently requires more than 1 secondary node. If this
787
    # changes, either this test must be skipped or the script must be updated.
788
    assert len(inodes) == 2
789
    snode = inodes[1]
790
  else:
791
    # instance is not redundant, but we still need to pass a node
792
    # (which will be ignored)
793
    snode = tnode
794
  pnode = inodes[0]
795
  # note: pnode:snode are the *current* nodes, so we move it first to
796
  # tnode:pnode, then back to pnode:snode
797
  for si, di, pn, sn in [(src_instance.name, dest_instance.name,
798
                          tnode.primary, pnode.primary),
799
                         (dest_instance.name, src_instance.name,
800
                          pnode.primary, snode.primary)]:
801
    cmd = [
802
      "../tools/move-instance",
803
      "--verbose",
804
      "--src-ca-file=%s" % _rapi_ca.name,
805
      "--src-username=%s" % _rapi_username,
806
      "--src-password-file=%s" % rapi_pw_file.name,
807
      "--dest-instance-name=%s" % di,
808
      "--dest-primary-node=%s" % pn,
809
      "--dest-secondary-node=%s" % sn,
810
      "--net=0:mac=%s" % constants.VALUE_GENERATE,
811
      master.primary,
812
      master.primary,
813
      si,
814
      ]
815

    
816
    qa_utils.RunInstanceCheck(di, False)
817
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
818
    qa_utils.RunInstanceCheck(si, False)
819
    qa_utils.RunInstanceCheck(di, True)