Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 5ae4945a

History | View | Annotate | Download (22.2 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
  if not isinstance(data, int):
152
    AssertMatch(data, r"^\d+$")
153

    
154

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

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

    
163

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

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

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

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

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

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

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

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

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

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

    
231

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
356

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

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

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

    
371
  def _VerifyInstancesBulk(data):
372
    for instance_data in data:
373
      _VerifyInstance(instance_data)
374

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

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

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

    
398

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

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

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

    
412
  def _VerifyNodesBulk(data):
413
    for node_data in data:
414
      _VerifyNode(node_data)
415

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

    
422

    
423
def _FilterTags(seq):
424
  """Removes unwanted tags from a sequence.
425

426
  """
427
  ignore_re = qa_config.get("ignore-tags-re", None)
428

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

    
434

    
435
def TestTags(kind, name, tags):
436
  """Tests .../tags resources.
437

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

    
450
  def _VerifyTags(data):
451
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
452

    
453
  queryargs = "&".join("tag=%s" % i for i in tags)
454

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

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

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

    
472

    
473
def _WaitForRapiJob(job_id):
474
  """Waits for a job to finish.
475

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

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

    
486
  return rapi.client_utils.PollJob(_rapi_client, job_id,
487
                                   cli.StdioJobPollReportCb())
488

    
489

    
490
def TestRapiNodeGroups():
491
  """Test several node group operations using RAPI.
492

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

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

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

    
507
  _WaitForRapiJob(job_id)
508

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

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

    
519
  _WaitForRapiJob(job_id)
520

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

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

    
530
  _WaitForRapiJob(job_id)
531

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

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

    
541
  _WaitForRapiJob(job_id)
542

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

    
549
    _WaitForRapiJob(job_id)
550

    
551

    
552
def TestRapiInstanceAdd(node, use_client):
553
  """Test adding a new instance via RAPI"""
554
  instance = qa_config.AcquireInstance()
555
  try:
556
    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
557
    disks = [{"size": size} for size in disk_sizes]
558
    nic0_mac = qa_config.GetInstanceNicMac(instance,
559
                                           default=constants.VALUE_GENERATE)
560
    nics = [{
561
      constants.INIC_MAC: nic0_mac,
562
      }]
563

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

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

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

    
594
    _WaitForRapiJob(job_id)
595

    
596
    return instance
597
  except:
598
    qa_config.ReleaseInstance(instance)
599
    raise
600

    
601

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

    
612
  _WaitForRapiJob(job_id)
613

    
614
  qa_config.ReleaseInstance(instance)
615

    
616

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

    
626

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

    
636

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

    
642

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

    
648

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

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

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

    
663

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

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

    
675

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

    
685

    
686
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
687
def TestRapiInstanceModify(instance):
688
  """Test modifying instance via RAPI"""
689
  default_hv = qa_config.GetDefaultHypervisor()
690

    
691
  def _ModifyInstance(**kwargs):
692
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
693

    
694
  _ModifyInstance(beparams={
695
    constants.BE_VCPUS: 3,
696
    })
697

    
698
  _ModifyInstance(beparams={
699
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
700
    })
701

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

    
717

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

    
726

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

    
738

    
739
def GetOperatingSystems():
740
  """Retrieves a list of all available operating systems.
741

742
  """
743
  return _rapi_client.GetOperatingSystems()
744

    
745

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

    
751
  rapi_pw_file = tempfile.NamedTemporaryFile()
752
  rapi_pw_file.write(_rapi_password)
753
  rapi_pw_file.flush()
754

    
755
  # TODO: Run some instance tests before moving back
756

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

    
784
    qa_utils.RunInstanceCheck(di, False)
785
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
786
    qa_utils.RunInstanceCheck(si, False)
787
    qa_utils.RunInstanceCheck(di, True)