Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 1490a90c

History | View | Annotate | Download (24 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
      # PUT with fields in query
328
      ("/2/query/%s?fields=%s" % (what, namefield),
329
       compat.partial(_Check, [namefield]), "PUT", {}),
330

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

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

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

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

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

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

    
376

    
377
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
378
def TestInstance(instance):
379
  """Testing getting instance(s) info via remote API.
380

381
  """
382
  def _VerifyInstance(data):
383
    for entry in INSTANCE_FIELDS:
384
      AssertIn(entry, data)
385

    
386
  def _VerifyInstancesList(data):
387
    for instance in data:
388
      for entry in LIST_FIELDS:
389
        AssertIn(entry, instance)
390

    
391
  def _VerifyInstancesBulk(data):
392
    for instance_data in data:
393
      _VerifyInstance(instance_data)
394

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

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

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

    
418

    
419
def TestNode(node):
420
  """Testing getting node(s) info via remote API.
421

422
  """
423
  def _VerifyNode(data):
424
    for entry in NODE_FIELDS:
425
      AssertIn(entry, data)
426

    
427
  def _VerifyNodesList(data):
428
    for node in data:
429
      for entry in LIST_FIELDS:
430
        AssertIn(entry, node)
431

    
432
  def _VerifyNodesBulk(data):
433
    for node_data in data:
434
      _VerifyNode(node_data)
435

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

    
442

    
443
def _FilterTags(seq):
444
  """Removes unwanted tags from a sequence.
445

446
  """
447
  ignore_re = qa_config.get("ignore-tags-re", None)
448

    
449
  if ignore_re:
450
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
451
  else:
452
    return seq
453

    
454

    
455
def TestTags(kind, name, tags):
456
  """Tests .../tags resources.
457

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

    
470
  def _VerifyTags(data):
471
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
472

    
473
  queryargs = "&".join("tag=%s" % i for i in tags)
474

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

    
481
  # Retrieve tags
482
  _DoTests([
483
    (uri, _VerifyTags, "GET", None),
484
    ])
485

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

    
492

    
493
def _WaitForRapiJob(job_id):
494
  """Waits for a job to finish.
495

496
  """
497
  def _VerifyJob(data):
498
    AssertEqual(data["id"], job_id)
499
    for field in JOB_FIELDS:
500
      AssertIn(field, data)
501

    
502
  _DoTests([
503
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
504
    ])
505

    
506
  return rapi.client_utils.PollJob(_rapi_client, job_id,
507
                                   cli.StdioJobPollReportCb())
508

    
509

    
510
def TestRapiNodeGroups():
511
  """Test several node group operations using RAPI.
512

513
  """
514
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
515

    
516
  # Create a group with no attributes
517
  body = {
518
    "name": group1,
519
    }
520

    
521
  (job_id, ) = _DoTests([
522
    ("/2/groups", _VerifyReturnsJob, "POST", body),
523
    ])
524

    
525
  _WaitForRapiJob(job_id)
526

    
527
  # Create a group specifying alloc_policy
528
  body = {
529
    "name": group2,
530
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
531
    }
532

    
533
  (job_id, ) = _DoTests([
534
    ("/2/groups", _VerifyReturnsJob, "POST", body),
535
    ])
536

    
537
  _WaitForRapiJob(job_id)
538

    
539
  # Modify alloc_policy
540
  body = {
541
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
542
    }
543

    
544
  (job_id, ) = _DoTests([
545
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
546
    ])
547

    
548
  _WaitForRapiJob(job_id)
549

    
550
  # Rename a group
551
  body = {
552
    "new_name": group3,
553
    }
554

    
555
  (job_id, ) = _DoTests([
556
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
557
    ])
558

    
559
  _WaitForRapiJob(job_id)
560

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

    
567
    _WaitForRapiJob(job_id)
568

    
569

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

    
585
    beparams = {
586
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
587
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
588
      }
589

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

    
611
      (job_id, ) = _DoTests([
612
        ("/2/instances", _VerifyReturnsJob, "POST", body),
613
        ])
614

    
615
    _WaitForRapiJob(job_id)
616

    
617
    return instance
618
  except:
619
    instance.Release()
620
    raise
621

    
622

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

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

    
638
  _WaitForRapiJob(job_id)
639

    
640

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

    
654

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

    
668

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

    
674

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

    
680

    
681
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
682
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
683
  """Test renaming instance via RAPI
684

685
  This must leave the instance with the original name (in the
686
  non-failure case).
687

688
  """
689
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
690
  qa_utils.RunInstanceCheck(rename_source, False)
691
  qa_utils.RunInstanceCheck(rename_target, False)
692
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
693
  qa_utils.RunInstanceCheck(rename_target, False)
694

    
695

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

    
703
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name))
704
  # By default, the instance is started again
705
  qa_utils.RunInstanceCheck(instance, True)
706

    
707
  # Reinstall again without starting
708
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name,
709
                                                 no_startup=True))
710

    
711

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

    
725

    
726
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
727
def TestRapiInstanceModify(instance):
728
  """Test modifying instance via RAPI"""
729
  default_hv = qa_config.GetDefaultHypervisor()
730

    
731
  def _ModifyInstance(**kwargs):
732
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance.name, **kwargs))
733

    
734
  _ModifyInstance(beparams={
735
    constants.BE_VCPUS: 3,
736
    })
737

    
738
  _ModifyInstance(beparams={
739
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
740
    })
741

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

    
757

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

    
766

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

    
778

    
779
def GetOperatingSystems():
780
  """Retrieves a list of all available operating systems.
781

782
  """
783
  return _rapi_client.GetOperatingSystems()
784

    
785

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

    
791
  rapi_pw_file = tempfile.NamedTemporaryFile()
792
  rapi_pw_file.write(_rapi_password)
793
  rapi_pw_file.flush()
794

    
795
  dest_instance.SetDiskTemplate(src_instance.disk_template)
796

    
797
  # TODO: Run some instance tests before moving back
798

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

    
830
    qa_utils.RunInstanceCheck(di, False)
831
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
832
    qa_utils.RunInstanceCheck(si, False)
833
    qa_utils.RunInstanceCheck(di, True)