Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ b4d2d2cb

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
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_utils import (AssertEqual, AssertIn, AssertMatch, StartLocalCommand)
50
from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG
51

    
52

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

    
58

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

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

    
70
  _rapi_username = username
71
  _rapi_password = password
72

    
73
  master = qa_config.GetMasterNode()
74

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

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

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

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

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

    
95

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

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

    
107
GROUP_FIELDS = compat.UniqueFrozenset([
108
  "name", "uuid",
109
  "alloc_policy",
110
  "node_cnt", "node_list",
111
  ])
112

    
113
JOB_FIELDS = compat.UniqueFrozenset([
114
  "id", "ops", "status", "summary",
115
  "opstatus", "opresult", "oplog",
116
  "received_ts", "start_ts", "end_ts",
117
  ])
118

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

    
121

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

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

    
128

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

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

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

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

    
146
    results.append(data)
147

    
148
  return results
149

    
150

    
151
def _VerifyReturnsJob(data):
152
  if not isinstance(data, int):
153
    AssertMatch(data, r"^\d+$")
154

    
155

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

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

    
164

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

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

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

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

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

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

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

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

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

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

    
232

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
357

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

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

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

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

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

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

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

    
399

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

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

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

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

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

    
423

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

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

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

    
435

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

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

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

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

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

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

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

    
473

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

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

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

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

    
490

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

494
  """
495
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
496

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

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

    
506
  _WaitForRapiJob(job_id)
507

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

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

    
518
  _WaitForRapiJob(job_id)
519

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

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

    
529
  _WaitForRapiJob(job_id)
530

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

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

    
540
  _WaitForRapiJob(job_id)
541

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

    
548
    _WaitForRapiJob(job_id)
549

    
550

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

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

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

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

    
593
    _WaitForRapiJob(job_id)
594

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

    
600

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

    
611
  _WaitForRapiJob(job_id)
612

    
613
  qa_config.ReleaseInstance(instance)
614

    
615

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

    
625

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

    
635

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

    
641

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

    
647

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

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

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

    
662

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

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

    
674

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

    
684

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

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

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

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

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

    
716

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

    
725

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

    
737

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

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

    
744

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

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

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

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

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