Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ b5ae26cd

History | View | Annotate | Download (24.3 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", "disk.spindles",
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", "sptotal", "spfree",
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
  # FIXME: the tests are failing if no LVM is enabled, investigate
250
  # if it is a bug in the QA or in the code
251
  if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
252
    return
253

    
254
  master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
255
  rnd = random.Random(7818)
256

    
257
  for what in constants.QR_VIA_RAPI:
258
    if what == constants.QR_JOB:
259
      namefield = "id"
260
      trivial_filter = [qlang.OP_GE, namefield, 0]
261
    elif what == constants.QR_EXPORT:
262
      namefield = "export"
263
      trivial_filter = [qlang.OP_REGEXP, ".*", namefield]
264
    else:
265
      namefield = "name"
266
      trivial_filter = [qlang.OP_REGEXP, ".*", namefield]
267

    
268
    all_fields = query.ALL_FIELDS[what].keys()
269
    rnd.shuffle(all_fields)
270

    
271
    # No fields, should return everything
272
    result = _rapi_client.QueryFields(what)
273
    qresult = objects.QueryFieldsResponse.FromDict(result)
274
    AssertEqual(len(qresult.fields), len(all_fields))
275

    
276
    # One field
277
    result = _rapi_client.QueryFields(what, fields=[namefield])
278
    qresult = objects.QueryFieldsResponse.FromDict(result)
279
    AssertEqual(len(qresult.fields), 1)
280

    
281
    # Specify all fields, order must be correct
282
    result = _rapi_client.QueryFields(what, fields=all_fields)
283
    qresult = objects.QueryFieldsResponse.FromDict(result)
284
    AssertEqual(len(qresult.fields), len(all_fields))
285
    AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
286

    
287
    # Unknown field
288
    result = _rapi_client.QueryFields(what, fields=["_unknown!"])
289
    qresult = objects.QueryFieldsResponse.FromDict(result)
290
    AssertEqual(len(qresult.fields), 1)
291
    AssertEqual(qresult.fields[0].name, "_unknown!")
292
    AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
293

    
294
    # Try once more, this time without the client
295
    _DoTests([
296
      ("/2/query/%s/fields" % what, None, "GET", None),
297
      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
298
       None, "GET", None),
299
      ])
300

    
301
    # Try missing query argument
302
    try:
303
      _DoTests([
304
        ("/2/query/%s" % what, None, "GET", None),
305
        ])
306
    except rapi.client.GanetiApiError, err:
307
      AssertEqual(err.code, 400)
308
    else:
309
      raise qa_error.Error("Request missing 'fields' parameter didn't fail")
310

    
311
    def _Check(exp_fields, data):
312
      qresult = objects.QueryResponse.FromDict(data)
313
      AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
314
      if not isinstance(qresult.data, list):
315
        raise qa_error.Error("Query did not return a list")
316

    
317
    _DoTests([
318
      # Specify fields in query
319
      ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
320
       compat.partial(_Check, all_fields), "GET", None),
321

    
322
      ("/2/query/%s?fields=%s" % (what, namefield),
323
       compat.partial(_Check, [namefield]), "GET", None),
324

    
325
      # Note the spaces
326
      ("/2/query/%s?fields=%s,%%20%s%%09,%s%%20" %
327
       (what, namefield, namefield, namefield),
328
       compat.partial(_Check, [namefield] * 3), "GET", None)])
329

    
330
    if what in constants.QR_VIA_RAPI_PUT:
331
      _DoTests([
332
        # PUT with fields in query
333
        ("/2/query/%s?fields=%s" % (what, namefield),
334
         compat.partial(_Check, [namefield]), "PUT", {}),
335

    
336
        ("/2/query/%s" % what, compat.partial(_Check, [namefield] * 4), "PUT", {
337
           "fields": [namefield] * 4,
338
           }),
339

    
340
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
341
           "fields": all_fields,
342
           }),
343

    
344
        ("/2/query/%s" % what, compat.partial(_Check, [namefield] * 4), "PUT", {
345
           "fields": [namefield] * 4
346
         })])
347

    
348
    def _CheckFilter():
349
      _DoTests([
350
        # With filter
351
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
352
           "fields": all_fields,
353
           "filter": trivial_filter
354
           }),
355
        ])
356

    
357
    if what == constants.QR_LOCK:
358
      # Locks can't be filtered
359
      try:
360
        _CheckFilter()
361
      except rapi.client.GanetiApiError, err:
362
        AssertEqual(err.code, 500)
363
      else:
364
        raise qa_error.Error("Filtering locks didn't fail")
365
    else:
366
      if what in constants.QR_VIA_RAPI_PUT:
367
        _CheckFilter()
368

    
369
    if what == constants.QR_NODE:
370
      # Test with filter
371
      (nodes, ) = _DoTests(
372
        [("/2/query/%s" % what,
373
          compat.partial(_Check, ["name", "master"]), "PUT",
374
          {"fields": ["name", "master"],
375
           "filter": [qlang.OP_TRUE, "master"],
376
           })])
377
      qresult = objects.QueryResponse.FromDict(nodes)
378
      AssertEqual(qresult.data, [
379
        [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
380
        ])
381

    
382

    
383
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
384
def TestInstance(instance):
385
  """Testing getting instance(s) info via remote API.
386

387
  """
388
  def _VerifyInstance(data):
389
    for entry in INSTANCE_FIELDS:
390
      AssertIn(entry, data)
391

    
392
  def _VerifyInstancesList(data):
393
    for instance in data:
394
      for entry in LIST_FIELDS:
395
        AssertIn(entry, instance)
396

    
397
  def _VerifyInstancesBulk(data):
398
    for instance_data in data:
399
      _VerifyInstance(instance_data)
400

    
401
  _DoTests([
402
    ("/2/instances/%s" % instance.name, _VerifyInstance, "GET", None),
403
    ("/2/instances", _VerifyInstancesList, "GET", None),
404
    ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
405
    ("/2/instances/%s/activate-disks" % instance.name,
406
     _VerifyReturnsJob, "PUT", None),
407
    ("/2/instances/%s/deactivate-disks" % instance.name,
408
     _VerifyReturnsJob, "PUT", None),
409
    ])
410

    
411
  # Test OpBackupPrepare
412
  (job_id, ) = _DoTests([
413
    ("/2/instances/%s/prepare-export?mode=%s" %
414
     (instance.name, constants.EXPORT_MODE_REMOTE),
415
     _VerifyReturnsJob, "PUT", None),
416
    ])
417

    
418
  result = _WaitForRapiJob(job_id)[0]
419
  AssertEqual(len(result["handshake"]), 3)
420
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
421
  AssertEqual(len(result["x509_key_name"]), 3)
422
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
423

    
424

    
425
def TestNode(node):
426
  """Testing getting node(s) info via remote API.
427

428
  """
429
  def _VerifyNode(data):
430
    for entry in NODE_FIELDS:
431
      AssertIn(entry, data)
432

    
433
  def _VerifyNodesList(data):
434
    for node in data:
435
      for entry in LIST_FIELDS:
436
        AssertIn(entry, node)
437

    
438
  def _VerifyNodesBulk(data):
439
    for node_data in data:
440
      _VerifyNode(node_data)
441

    
442
  _DoTests([
443
    ("/2/nodes/%s" % node.primary, _VerifyNode, "GET", None),
444
    ("/2/nodes", _VerifyNodesList, "GET", None),
445
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
446
    ])
447

    
448

    
449
def _FilterTags(seq):
450
  """Removes unwanted tags from a sequence.
451

452
  """
453
  ignore_re = qa_config.get("ignore-tags-re", None)
454

    
455
  if ignore_re:
456
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
457
  else:
458
    return seq
459

    
460

    
461
def TestTags(kind, name, tags):
462
  """Tests .../tags resources.
463

464
  """
465
  if kind == constants.TAG_CLUSTER:
466
    uri = "/2/tags"
467
  elif kind == constants.TAG_NODE:
468
    uri = "/2/nodes/%s/tags" % name
469
  elif kind == constants.TAG_INSTANCE:
470
    uri = "/2/instances/%s/tags" % name
471
  elif kind == constants.TAG_NODEGROUP:
472
    uri = "/2/groups/%s/tags" % name
473
  else:
474
    raise errors.ProgrammerError("Unknown tag kind")
475

    
476
  def _VerifyTags(data):
477
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
478

    
479
  queryargs = "&".join("tag=%s" % i for i in tags)
480

    
481
  # Add tags
482
  (job_id, ) = _DoTests([
483
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
484
    ])
485
  _WaitForRapiJob(job_id)
486

    
487
  # Retrieve tags
488
  _DoTests([
489
    (uri, _VerifyTags, "GET", None),
490
    ])
491

    
492
  # Remove tags
493
  (job_id, ) = _DoTests([
494
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "DELETE", None),
495
    ])
496
  _WaitForRapiJob(job_id)
497

    
498

    
499
def _WaitForRapiJob(job_id):
500
  """Waits for a job to finish.
501

502
  """
503
  def _VerifyJob(data):
504
    AssertEqual(data["id"], job_id)
505
    for field in JOB_FIELDS:
506
      AssertIn(field, data)
507

    
508
  _DoTests([
509
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
510
    ])
511

    
512
  return rapi.client_utils.PollJob(_rapi_client, job_id,
513
                                   cli.StdioJobPollReportCb())
514

    
515

    
516
def TestRapiNodeGroups():
517
  """Test several node group operations using RAPI.
518

519
  """
520
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
521

    
522
  # Create a group with no attributes
523
  body = {
524
    "name": group1,
525
    }
526

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

    
531
  _WaitForRapiJob(job_id)
532

    
533
  # Create a group specifying alloc_policy
534
  body = {
535
    "name": group2,
536
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
537
    }
538

    
539
  (job_id, ) = _DoTests([
540
    ("/2/groups", _VerifyReturnsJob, "POST", body),
541
    ])
542

    
543
  _WaitForRapiJob(job_id)
544

    
545
  # Modify alloc_policy
546
  body = {
547
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
548
    }
549

    
550
  (job_id, ) = _DoTests([
551
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
552
    ])
553

    
554
  _WaitForRapiJob(job_id)
555

    
556
  # Rename a group
557
  body = {
558
    "new_name": group3,
559
    }
560

    
561
  (job_id, ) = _DoTests([
562
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
563
    ])
564

    
565
  _WaitForRapiJob(job_id)
566

    
567
  # Delete groups
568
  for group in [group1, group3]:
569
    (job_id, ) = _DoTests([
570
      ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
571
      ])
572

    
573
    _WaitForRapiJob(job_id)
574

    
575

    
576
def TestRapiInstanceAdd(node, use_client):
577
  """Test adding a new instance via RAPI"""
578
  if not qa_config.IsTemplateSupported(constants.DT_PLAIN):
579
    return
580
  instance = qa_config.AcquireInstance()
581
  instance.SetDiskTemplate(constants.DT_PLAIN)
582
  try:
583
    disks = [{"size": utils.ParseUnit(d.get("size")),
584
              "name": str(d.get("name"))}
585
             for d in qa_config.GetDiskOptions()]
586
    nic0_mac = instance.GetNicMacAddr(0, constants.VALUE_GENERATE)
587
    nics = [{
588
      constants.INIC_MAC: nic0_mac,
589
      }]
590

    
591
    beparams = {
592
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
593
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
594
      }
595

    
596
    if use_client:
597
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
598
                                           instance.name,
599
                                           constants.DT_PLAIN,
600
                                           disks, nics,
601
                                           os=qa_config.get("os"),
602
                                           pnode=node.primary,
603
                                           beparams=beparams)
604
    else:
605
      body = {
606
        "__version__": 1,
607
        "mode": constants.INSTANCE_CREATE,
608
        "name": instance.name,
609
        "os_type": qa_config.get("os"),
610
        "disk_template": constants.DT_PLAIN,
611
        "pnode": node.primary,
612
        "beparams": beparams,
613
        "disks": disks,
614
        "nics": nics,
615
        }
616

    
617
      (job_id, ) = _DoTests([
618
        ("/2/instances", _VerifyReturnsJob, "POST", body),
619
        ])
620

    
621
    _WaitForRapiJob(job_id)
622

    
623
    return instance
624
  except:
625
    instance.Release()
626
    raise
627

    
628

    
629
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
630
def TestRapiInstanceRemove(instance, use_client):
631
  """Test removing instance via RAPI"""
632
  # FIXME: this does not work if LVM is not enabled. Find out if this is a bug
633
  # in RAPI or in the test
634
  if not qa_config.IsStorageTypeSupported(constants.ST_LVM_VG):
635
    return
636

    
637
  if use_client:
638
    job_id = _rapi_client.DeleteInstance(instance.name)
639
  else:
640
    (job_id, ) = _DoTests([
641
      ("/2/instances/%s" % instance.name, _VerifyReturnsJob, "DELETE", None),
642
      ])
643

    
644
  _WaitForRapiJob(job_id)
645

    
646

    
647
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
648
def TestRapiInstanceMigrate(instance):
649
  """Test migrating instance via RAPI"""
650
  if not IsMigrationSupported(instance):
651
    print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
652
                              " test")
653
    return
654
  # Move to secondary node
655
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance.name))
656
  qa_utils.RunInstanceCheck(instance, True)
657
  # And back to previous primary
658
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance.name))
659

    
660

    
661
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
662
def TestRapiInstanceFailover(instance):
663
  """Test failing over instance via RAPI"""
664
  if not IsFailoverSupported(instance):
665
    print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
666
                              " test")
667
    return
668
  # Move to secondary node
669
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance.name))
670
  qa_utils.RunInstanceCheck(instance, True)
671
  # And back to previous primary
672
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance.name))
673

    
674

    
675
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
676
def TestRapiInstanceShutdown(instance):
677
  """Test stopping an instance via RAPI"""
678
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance.name))
679

    
680

    
681
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
682
def TestRapiInstanceStartup(instance):
683
  """Test starting an instance via RAPI"""
684
  _WaitForRapiJob(_rapi_client.StartupInstance(instance.name))
685

    
686

    
687
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
688
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
689
  """Test renaming instance via RAPI
690

691
  This must leave the instance with the original name (in the
692
  non-failure case).
693

694
  """
695
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
696
  qa_utils.RunInstanceCheck(rename_source, False)
697
  qa_utils.RunInstanceCheck(rename_target, False)
698
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
699
  qa_utils.RunInstanceCheck(rename_target, False)
700

    
701

    
702
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
703
def TestRapiInstanceReinstall(instance):
704
  """Test reinstalling an instance via RAPI"""
705
  if instance.disk_template == constants.DT_DISKLESS:
706
    print qa_utils.FormatInfo("Test not supported for diskless instances")
707
    return
708

    
709
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name))
710
  # By default, the instance is started again
711
  qa_utils.RunInstanceCheck(instance, True)
712

    
713
  # Reinstall again without starting
714
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name,
715
                                                 no_startup=True))
716

    
717

    
718
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
719
def TestRapiInstanceReplaceDisks(instance):
720
  """Test replacing instance disks via RAPI"""
721
  if not IsDiskReplacingSupported(instance):
722
    print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
723
                              " skipping test")
724
    return
725
  fn = _rapi_client.ReplaceInstanceDisks
726
  _WaitForRapiJob(fn(instance.name,
727
                     mode=constants.REPLACE_DISK_AUTO, disks=[]))
728
  _WaitForRapiJob(fn(instance.name,
729
                     mode=constants.REPLACE_DISK_SEC, disks="0"))
730

    
731

    
732
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
733
def TestRapiInstanceModify(instance):
734
  """Test modifying instance via RAPI"""
735
  default_hv = qa_config.GetDefaultHypervisor()
736

    
737
  def _ModifyInstance(**kwargs):
738
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance.name, **kwargs))
739

    
740
  _ModifyInstance(beparams={
741
    constants.BE_VCPUS: 3,
742
    })
743

    
744
  _ModifyInstance(beparams={
745
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
746
    })
747

    
748
  if default_hv == constants.HT_XEN_PVM:
749
    _ModifyInstance(hvparams={
750
      constants.HV_KERNEL_ARGS: "single",
751
      })
752
    _ModifyInstance(hvparams={
753
      constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
754
      })
755
  elif default_hv == constants.HT_XEN_HVM:
756
    _ModifyInstance(hvparams={
757
      constants.HV_BOOT_ORDER: "acn",
758
      })
759
    _ModifyInstance(hvparams={
760
      constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
761
      })
762

    
763

    
764
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
765
def TestRapiInstanceConsole(instance):
766
  """Test getting instance console information via RAPI"""
767
  result = _rapi_client.GetInstanceConsole(instance.name)
768
  console = objects.InstanceConsole.FromDict(result)
769
  AssertEqual(console.Validate(), True)
770
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance.name))
771

    
772

    
773
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
774
def TestRapiStoppedInstanceConsole(instance):
775
  """Test getting stopped instance's console information via RAPI"""
776
  try:
777
    _rapi_client.GetInstanceConsole(instance.name)
778
  except rapi.client.GanetiApiError, err:
779
    AssertEqual(err.code, 503)
780
  else:
781
    raise qa_error.Error("Getting console for stopped instance didn't"
782
                         " return HTTP 503")
783

    
784

    
785
def GetOperatingSystems():
786
  """Retrieves a list of all available operating systems.
787

788
  """
789
  return _rapi_client.GetOperatingSystems()
790

    
791

    
792
def TestInterClusterInstanceMove(src_instance, dest_instance,
793
                                 inodes, tnode):
794
  """Test tools/move-instance"""
795
  master = qa_config.GetMasterNode()
796

    
797
  rapi_pw_file = tempfile.NamedTemporaryFile()
798
  rapi_pw_file.write(_rapi_password)
799
  rapi_pw_file.flush()
800

    
801
  dest_instance.SetDiskTemplate(src_instance.disk_template)
802

    
803
  # TODO: Run some instance tests before moving back
804

    
805
  if len(inodes) > 1:
806
    # No disk template currently requires more than 1 secondary node. If this
807
    # changes, either this test must be skipped or the script must be updated.
808
    assert len(inodes) == 2
809
    snode = inodes[1]
810
  else:
811
    # instance is not redundant, but we still need to pass a node
812
    # (which will be ignored)
813
    snode = tnode
814
  pnode = inodes[0]
815
  # note: pnode:snode are the *current* nodes, so we move it first to
816
  # tnode:pnode, then back to pnode:snode
817
  for si, di, pn, sn in [(src_instance.name, dest_instance.name,
818
                          tnode.primary, pnode.primary),
819
                         (dest_instance.name, src_instance.name,
820
                          pnode.primary, snode.primary)]:
821
    cmd = [
822
      "../tools/move-instance",
823
      "--verbose",
824
      "--src-ca-file=%s" % _rapi_ca.name,
825
      "--src-username=%s" % _rapi_username,
826
      "--src-password-file=%s" % rapi_pw_file.name,
827
      "--dest-instance-name=%s" % di,
828
      "--dest-primary-node=%s" % pn,
829
      "--dest-secondary-node=%s" % sn,
830
      "--net=0:mac=%s" % constants.VALUE_GENERATE,
831
      master.primary,
832
      master.primary,
833
      si,
834
      ]
835

    
836
    qa_utils.RunInstanceCheck(di, False)
837
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
838
    qa_utils.RunInstanceCheck(si, False)
839
    qa_utils.RunInstanceCheck(di, True)