Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ ad658827

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
    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
569
    disks = [{"size": size} for size in disk_sizes]
570
    nic0_mac = instance.GetNicMacAddr(0, constants.VALUE_GENERATE)
571
    nics = [{
572
      constants.INIC_MAC: nic0_mac,
573
      }]
574

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

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

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

    
605
    _WaitForRapiJob(job_id)
606

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

    
612

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

    
623
  _WaitForRapiJob(job_id)
624

    
625

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

    
639

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

    
653

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

    
659

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

    
665

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

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

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

    
680

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

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

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

    
696

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

    
710

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

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

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

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

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

    
742

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

    
751

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

    
763

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

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

    
770

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

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

    
780
  dest_instance.SetDiskTemplate(src_instance.disk_template)
781

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

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

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