Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ a8dbf746

History | View | Annotate | Download (22.1 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
    nic0_mac = qa_config.GetInstanceNicMac(instance,
557
                                           default=constants.VALUE_GENERATE)
558
    nics = [{
559
      constants.INIC_MAC: nic0_mac,
560
      }]
561

    
562
    beparams = {
563
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
564
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
565
      }
566

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

    
588
      (job_id, ) = _DoTests([
589
        ("/2/instances", _VerifyReturnsJob, "POST", body),
590
        ])
591

    
592
    _WaitForRapiJob(job_id)
593

    
594
    return instance
595
  except:
596
    qa_config.ReleaseInstance(instance)
597
    raise
598

    
599

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

    
610
  _WaitForRapiJob(job_id)
611

    
612
  qa_config.ReleaseInstance(instance)
613

    
614

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

    
624

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

    
634

    
635
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
636
def TestRapiInstanceShutdown(instance):
637
  """Test stopping an instance via RAPI"""
638
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
639

    
640

    
641
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
642
def TestRapiInstanceStartup(instance):
643
  """Test starting an instance via RAPI"""
644
  _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
645

    
646

    
647
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
648
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
649
  """Test renaming instance via RAPI
650

651
  This must leave the instance with the original name (in the
652
  non-failure case).
653

654
  """
655
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
656
  qa_utils.RunInstanceCheck(rename_source, False)
657
  qa_utils.RunInstanceCheck(rename_target, False)
658
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
659
  qa_utils.RunInstanceCheck(rename_target, False)
660

    
661

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

    
669
  # Reinstall again without starting
670
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"],
671
                                                 no_startup=True))
672

    
673

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

    
682

    
683
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
684
def TestRapiInstanceModify(instance):
685
  """Test modifying instance via RAPI"""
686
  default_hv = qa_config.GetDefaultHypervisor()
687

    
688
  def _ModifyInstance(**kwargs):
689
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
690

    
691
  _ModifyInstance(beparams={
692
    constants.BE_VCPUS: 3,
693
    })
694

    
695
  _ModifyInstance(beparams={
696
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
697
    })
698

    
699
  if default_hv == constants.HT_XEN_PVM:
700
    _ModifyInstance(hvparams={
701
      constants.HV_KERNEL_ARGS: "single",
702
      })
703
    _ModifyInstance(hvparams={
704
      constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
705
      })
706
  elif default_hv == constants.HT_XEN_HVM:
707
    _ModifyInstance(hvparams={
708
      constants.HV_BOOT_ORDER: "acn",
709
      })
710
    _ModifyInstance(hvparams={
711
      constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
712
      })
713

    
714

    
715
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
716
def TestRapiInstanceConsole(instance):
717
  """Test getting instance console information via RAPI"""
718
  result = _rapi_client.GetInstanceConsole(instance["name"])
719
  console = objects.InstanceConsole.FromDict(result)
720
  AssertEqual(console.Validate(), True)
721
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
722

    
723

    
724
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
725
def TestRapiStoppedInstanceConsole(instance):
726
  """Test getting stopped instance's console information via RAPI"""
727
  try:
728
    _rapi_client.GetInstanceConsole(instance["name"])
729
  except rapi.client.GanetiApiError, err:
730
    AssertEqual(err.code, 503)
731
  else:
732
    raise qa_error.Error("Getting console for stopped instance didn't"
733
                         " return HTTP 503")
734

    
735

    
736
def GetOperatingSystems():
737
  """Retrieves a list of all available operating systems.
738

739
  """
740
  return _rapi_client.GetOperatingSystems()
741

    
742

    
743
def TestInterClusterInstanceMove(src_instance, dest_instance,
744
                                 pnode, snode, tnode):
745
  """Test tools/move-instance"""
746
  master = qa_config.GetMasterNode()
747

    
748
  rapi_pw_file = tempfile.NamedTemporaryFile()
749
  rapi_pw_file.write(_rapi_password)
750
  rapi_pw_file.flush()
751

    
752
  # TODO: Run some instance tests before moving back
753

    
754
  if snode is None:
755
    # instance is not redundant, but we still need to pass a node
756
    # (which will be ignored)
757
    fsec = tnode
758
  else:
759
    fsec = snode
760
  # note: pnode:snode are the *current* nodes, so we move it first to
761
  # tnode:pnode, then back to pnode:snode
762
  for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
763
                          tnode["primary"], pnode["primary"]),
764
                         (dest_instance["name"], src_instance["name"],
765
                          pnode["primary"], fsec["primary"])]:
766
    cmd = [
767
      "../tools/move-instance",
768
      "--verbose",
769
      "--src-ca-file=%s" % _rapi_ca.name,
770
      "--src-username=%s" % _rapi_username,
771
      "--src-password-file=%s" % rapi_pw_file.name,
772
      "--dest-instance-name=%s" % di,
773
      "--dest-primary-node=%s" % pn,
774
      "--dest-secondary-node=%s" % sn,
775
      "--net=0:mac=%s" % constants.VALUE_GENERATE,
776
      master["primary"],
777
      master["primary"],
778
      si,
779
      ]
780

    
781
    qa_utils.RunInstanceCheck(di, False)
782
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
783
    qa_utils.RunInstanceCheck(si, False)
784
    qa_utils.RunInstanceCheck(di, True)