Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 543937ce

History | View | Annotate | Download (24.1 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
    elif what == constants.QR_EXPORT:
261
      namefield = "export"
262
    else:
263
      namefield = "name"
264

    
265
    all_fields = query.ALL_FIELDS[what].keys()
266
    rnd.shuffle(all_fields)
267

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

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

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

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

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

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

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

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

    
319
      ("/2/query/%s?fields=%s" % (what, namefield),
320
       compat.partial(_Check, [namefield]), "GET", None),
321

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

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

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

    
337
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
338
           "fields": all_fields,
339
           }),
340

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

    
345
    def _CheckFilter():
346
      _DoTests([
347
        # With filter
348
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
349
           "fields": all_fields,
350
           "filter": [qlang.OP_TRUE, namefield],
351
           }),
352
        ])
353

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

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

    
379

    
380
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
381
def TestInstance(instance):
382
  """Testing getting instance(s) info via remote API.
383

384
  """
385
  def _VerifyInstance(data):
386
    for entry in INSTANCE_FIELDS:
387
      AssertIn(entry, data)
388

    
389
  def _VerifyInstancesList(data):
390
    for instance in data:
391
      for entry in LIST_FIELDS:
392
        AssertIn(entry, instance)
393

    
394
  def _VerifyInstancesBulk(data):
395
    for instance_data in data:
396
      _VerifyInstance(instance_data)
397

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

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

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

    
421

    
422
def TestNode(node):
423
  """Testing getting node(s) info via remote API.
424

425
  """
426
  def _VerifyNode(data):
427
    for entry in NODE_FIELDS:
428
      AssertIn(entry, data)
429

    
430
  def _VerifyNodesList(data):
431
    for node in data:
432
      for entry in LIST_FIELDS:
433
        AssertIn(entry, node)
434

    
435
  def _VerifyNodesBulk(data):
436
    for node_data in data:
437
      _VerifyNode(node_data)
438

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

    
445

    
446
def _FilterTags(seq):
447
  """Removes unwanted tags from a sequence.
448

449
  """
450
  ignore_re = qa_config.get("ignore-tags-re", None)
451

    
452
  if ignore_re:
453
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
454
  else:
455
    return seq
456

    
457

    
458
def TestTags(kind, name, tags):
459
  """Tests .../tags resources.
460

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

    
473
  def _VerifyTags(data):
474
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
475

    
476
  queryargs = "&".join("tag=%s" % i for i in tags)
477

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

    
484
  # Retrieve tags
485
  _DoTests([
486
    (uri, _VerifyTags, "GET", None),
487
    ])
488

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

    
495

    
496
def _WaitForRapiJob(job_id):
497
  """Waits for a job to finish.
498

499
  """
500
  def _VerifyJob(data):
501
    AssertEqual(data["id"], job_id)
502
    for field in JOB_FIELDS:
503
      AssertIn(field, data)
504

    
505
  _DoTests([
506
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
507
    ])
508

    
509
  return rapi.client_utils.PollJob(_rapi_client, job_id,
510
                                   cli.StdioJobPollReportCb())
511

    
512

    
513
def TestRapiNodeGroups():
514
  """Test several node group operations using RAPI.
515

516
  """
517
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
518

    
519
  # Create a group with no attributes
520
  body = {
521
    "name": group1,
522
    }
523

    
524
  (job_id, ) = _DoTests([
525
    ("/2/groups", _VerifyReturnsJob, "POST", body),
526
    ])
527

    
528
  _WaitForRapiJob(job_id)
529

    
530
  # Create a group specifying alloc_policy
531
  body = {
532
    "name": group2,
533
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
534
    }
535

    
536
  (job_id, ) = _DoTests([
537
    ("/2/groups", _VerifyReturnsJob, "POST", body),
538
    ])
539

    
540
  _WaitForRapiJob(job_id)
541

    
542
  # Modify alloc_policy
543
  body = {
544
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
545
    }
546

    
547
  (job_id, ) = _DoTests([
548
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
549
    ])
550

    
551
  _WaitForRapiJob(job_id)
552

    
553
  # Rename a group
554
  body = {
555
    "new_name": group3,
556
    }
557

    
558
  (job_id, ) = _DoTests([
559
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
560
    ])
561

    
562
  _WaitForRapiJob(job_id)
563

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

    
570
    _WaitForRapiJob(job_id)
571

    
572

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

    
588
    beparams = {
589
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
590
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
591
      }
592

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

    
614
      (job_id, ) = _DoTests([
615
        ("/2/instances", _VerifyReturnsJob, "POST", body),
616
        ])
617

    
618
    _WaitForRapiJob(job_id)
619

    
620
    return instance
621
  except:
622
    instance.Release()
623
    raise
624

    
625

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

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

    
641
  _WaitForRapiJob(job_id)
642

    
643

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

    
657

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

    
671

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

    
677

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

    
683

    
684
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
685
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
686
  """Test renaming instance via RAPI
687

688
  This must leave the instance with the original name (in the
689
  non-failure case).
690

691
  """
692
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
693
  qa_utils.RunInstanceCheck(rename_source, False)
694
  qa_utils.RunInstanceCheck(rename_target, False)
695
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
696
  qa_utils.RunInstanceCheck(rename_target, False)
697

    
698

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

    
706
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name))
707
  # By default, the instance is started again
708
  qa_utils.RunInstanceCheck(instance, True)
709

    
710
  # Reinstall again without starting
711
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name,
712
                                                 no_startup=True))
713

    
714

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

    
728

    
729
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
730
def TestRapiInstanceModify(instance):
731
  """Test modifying instance via RAPI"""
732
  default_hv = qa_config.GetDefaultHypervisor()
733

    
734
  def _ModifyInstance(**kwargs):
735
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance.name, **kwargs))
736

    
737
  _ModifyInstance(beparams={
738
    constants.BE_VCPUS: 3,
739
    })
740

    
741
  _ModifyInstance(beparams={
742
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
743
    })
744

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

    
760

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

    
769

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

    
781

    
782
def GetOperatingSystems():
783
  """Retrieves a list of all available operating systems.
784

785
  """
786
  return _rapi_client.GetOperatingSystems()
787

    
788

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

    
794
  rapi_pw_file = tempfile.NamedTemporaryFile()
795
  rapi_pw_file.write(_rapi_password)
796
  rapi_pw_file.flush()
797

    
798
  dest_instance.SetDiskTemplate(src_instance.disk_template)
799

    
800
  # TODO: Run some instance tests before moving back
801

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

    
833
    qa_utils.RunInstanceCheck(di, False)
834
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
835
    qa_utils.RunInstanceCheck(si, False)
836
    qa_utils.RunInstanceCheck(di, True)