Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ a4c589d2

History | View | Annotate | Download (23.5 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",
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",
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
  master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
250
  rnd = random.Random(7818)
251

    
252
  for what in constants.QR_VIA_RAPI:
253
    if what == constants.QR_JOB:
254
      namefield = "id"
255
    elif what == constants.QR_EXPORT:
256
      namefield = "export"
257
    else:
258
      namefield = "name"
259

    
260
    all_fields = query.ALL_FIELDS[what].keys()
261
    rnd.shuffle(all_fields)
262

    
263
    # No fields, should return everything
264
    result = _rapi_client.QueryFields(what)
265
    qresult = objects.QueryFieldsResponse.FromDict(result)
266
    AssertEqual(len(qresult.fields), len(all_fields))
267

    
268
    # One field
269
    result = _rapi_client.QueryFields(what, fields=[namefield])
270
    qresult = objects.QueryFieldsResponse.FromDict(result)
271
    AssertEqual(len(qresult.fields), 1)
272

    
273
    # Specify all fields, order must be correct
274
    result = _rapi_client.QueryFields(what, fields=all_fields)
275
    qresult = objects.QueryFieldsResponse.FromDict(result)
276
    AssertEqual(len(qresult.fields), len(all_fields))
277
    AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
278

    
279
    # Unknown field
280
    result = _rapi_client.QueryFields(what, fields=["_unknown!"])
281
    qresult = objects.QueryFieldsResponse.FromDict(result)
282
    AssertEqual(len(qresult.fields), 1)
283
    AssertEqual(qresult.fields[0].name, "_unknown!")
284
    AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
285

    
286
    # Try once more, this time without the client
287
    _DoTests([
288
      ("/2/query/%s/fields" % what, None, "GET", None),
289
      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
290
       None, "GET", None),
291
      ])
292

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

    
303
    def _Check(exp_fields, data):
304
      qresult = objects.QueryResponse.FromDict(data)
305
      AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
306
      if not isinstance(qresult.data, list):
307
        raise qa_error.Error("Query did not return a list")
308

    
309
    _DoTests([
310
      # Specify fields in query
311
      ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
312
       compat.partial(_Check, all_fields), "GET", None),
313

    
314
      ("/2/query/%s?fields=%s" % (what, namefield),
315
       compat.partial(_Check, [namefield]), "GET", None),
316

    
317
      # Note the spaces
318
      ("/2/query/%s?fields=%s,%%20%s%%09,%s%%20" %
319
       (what, namefield, namefield, namefield),
320
       compat.partial(_Check, [namefield] * 3), "GET", None),
321

    
322
      # PUT with fields in query
323
      ("/2/query/%s?fields=%s" % (what, namefield),
324
       compat.partial(_Check, [namefield]), "PUT", {}),
325

    
326
      # Fields in body
327
      ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
328
         "fields": all_fields,
329
         }),
330

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

    
336
    def _CheckFilter():
337
      _DoTests([
338
        # With filter
339
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
340
           "fields": all_fields,
341
           "filter": [qlang.OP_TRUE, namefield],
342
           }),
343
        ])
344

    
345
    if what == constants.QR_LOCK:
346
      # Locks can't be filtered
347
      try:
348
        _CheckFilter()
349
      except rapi.client.GanetiApiError, err:
350
        AssertEqual(err.code, 500)
351
      else:
352
        raise qa_error.Error("Filtering locks didn't fail")
353
    else:
354
      _CheckFilter()
355

    
356
    if what == constants.QR_NODE:
357
      # Test with filter
358
      (nodes, ) = _DoTests(
359
        [("/2/query/%s" % what,
360
          compat.partial(_Check, ["name", "master"]), "PUT",
361
          {"fields": ["name", "master"],
362
           "filter": [qlang.OP_TRUE, "master"],
363
           })])
364
      qresult = objects.QueryResponse.FromDict(nodes)
365
      AssertEqual(qresult.data, [
366
        [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
367
        ])
368

    
369

    
370
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
371
def TestInstance(instance):
372
  """Testing getting instance(s) info via remote API.
373

374
  """
375
  def _VerifyInstance(data):
376
    for entry in INSTANCE_FIELDS:
377
      AssertIn(entry, data)
378

    
379
  def _VerifyInstancesList(data):
380
    for instance in data:
381
      for entry in LIST_FIELDS:
382
        AssertIn(entry, instance)
383

    
384
  def _VerifyInstancesBulk(data):
385
    for instance_data in data:
386
      _VerifyInstance(instance_data)
387

    
388
  _DoTests([
389
    ("/2/instances/%s" % instance.name, _VerifyInstance, "GET", None),
390
    ("/2/instances", _VerifyInstancesList, "GET", None),
391
    ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
392
    ("/2/instances/%s/activate-disks" % instance.name,
393
     _VerifyReturnsJob, "PUT", None),
394
    ("/2/instances/%s/deactivate-disks" % instance.name,
395
     _VerifyReturnsJob, "PUT", None),
396
    ])
397

    
398
  # Test OpBackupPrepare
399
  (job_id, ) = _DoTests([
400
    ("/2/instances/%s/prepare-export?mode=%s" %
401
     (instance.name, constants.EXPORT_MODE_REMOTE),
402
     _VerifyReturnsJob, "PUT", None),
403
    ])
404

    
405
  result = _WaitForRapiJob(job_id)[0]
406
  AssertEqual(len(result["handshake"]), 3)
407
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
408
  AssertEqual(len(result["x509_key_name"]), 3)
409
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
410

    
411

    
412
def TestNode(node):
413
  """Testing getting node(s) info via remote API.
414

415
  """
416
  def _VerifyNode(data):
417
    for entry in NODE_FIELDS:
418
      AssertIn(entry, data)
419

    
420
  def _VerifyNodesList(data):
421
    for node in data:
422
      for entry in LIST_FIELDS:
423
        AssertIn(entry, node)
424

    
425
  def _VerifyNodesBulk(data):
426
    for node_data in data:
427
      _VerifyNode(node_data)
428

    
429
  _DoTests([
430
    ("/2/nodes/%s" % node.primary, _VerifyNode, "GET", None),
431
    ("/2/nodes", _VerifyNodesList, "GET", None),
432
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
433
    ])
434

    
435

    
436
def _FilterTags(seq):
437
  """Removes unwanted tags from a sequence.
438

439
  """
440
  ignore_re = qa_config.get("ignore-tags-re", None)
441

    
442
  if ignore_re:
443
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
444
  else:
445
    return seq
446

    
447

    
448
def TestTags(kind, name, tags):
449
  """Tests .../tags resources.
450

451
  """
452
  if kind == constants.TAG_CLUSTER:
453
    uri = "/2/tags"
454
  elif kind == constants.TAG_NODE:
455
    uri = "/2/nodes/%s/tags" % name
456
  elif kind == constants.TAG_INSTANCE:
457
    uri = "/2/instances/%s/tags" % name
458
  elif kind == constants.TAG_NODEGROUP:
459
    uri = "/2/groups/%s/tags" % name
460
  elif kind == constants.TAG_NETWORK:
461
    uri = "/2/networks/%s/tags" % name
462
  else:
463
    raise errors.ProgrammerError("Unknown tag kind")
464

    
465
  def _VerifyTags(data):
466
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
467

    
468
  queryargs = "&".join("tag=%s" % i for i in tags)
469

    
470
  # Add tags
471
  (job_id, ) = _DoTests([
472
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
473
    ])
474
  _WaitForRapiJob(job_id)
475

    
476
  # Retrieve tags
477
  _DoTests([
478
    (uri, _VerifyTags, "GET", None),
479
    ])
480

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

    
487

    
488
def _WaitForRapiJob(job_id):
489
  """Waits for a job to finish.
490

491
  """
492
  def _VerifyJob(data):
493
    AssertEqual(data["id"], job_id)
494
    for field in JOB_FIELDS:
495
      AssertIn(field, data)
496

    
497
  _DoTests([
498
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
499
    ])
500

    
501
  return rapi.client_utils.PollJob(_rapi_client, job_id,
502
                                   cli.StdioJobPollReportCb())
503

    
504

    
505
def TestRapiNodeGroups():
506
  """Test several node group operations using RAPI.
507

508
  """
509
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
510

    
511
  # Create a group with no attributes
512
  body = {
513
    "name": group1,
514
    }
515

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

    
520
  _WaitForRapiJob(job_id)
521

    
522
  # Create a group specifying alloc_policy
523
  body = {
524
    "name": group2,
525
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
526
    }
527

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

    
532
  _WaitForRapiJob(job_id)
533

    
534
  # Modify alloc_policy
535
  body = {
536
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
537
    }
538

    
539
  (job_id, ) = _DoTests([
540
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
541
    ])
542

    
543
  _WaitForRapiJob(job_id)
544

    
545
  # Rename a group
546
  body = {
547
    "new_name": group3,
548
    }
549

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

    
554
  _WaitForRapiJob(job_id)
555

    
556
  # Delete groups
557
  for group in [group1, group3]:
558
    (job_id, ) = _DoTests([
559
      ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
560
      ])
561

    
562
    _WaitForRapiJob(job_id)
563

    
564

    
565
def TestRapiInstanceAdd(node, use_client):
566
  """Test adding a new instance via RAPI"""
567
  instance = qa_config.AcquireInstance()
568
  instance.SetDiskTemplate(constants.DT_PLAIN)
569
  try:
570
    disks = [{"size": utils.ParseUnit(d.get("size")),
571
              "name": str(d.get("name"))}
572
             for d in qa_config.GetDiskOptions()]
573
    nic0_mac = instance.GetNicMacAddr(0, constants.VALUE_GENERATE)
574
    nics = [{
575
      constants.INIC_MAC: nic0_mac,
576
      }]
577

    
578
    beparams = {
579
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
580
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
581
      }
582

    
583
    if use_client:
584
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
585
                                           instance.name,
586
                                           constants.DT_PLAIN,
587
                                           disks, nics,
588
                                           os=qa_config.get("os"),
589
                                           pnode=node.primary,
590
                                           beparams=beparams)
591
    else:
592
      body = {
593
        "__version__": 1,
594
        "mode": constants.INSTANCE_CREATE,
595
        "name": instance.name,
596
        "os_type": qa_config.get("os"),
597
        "disk_template": constants.DT_PLAIN,
598
        "pnode": node.primary,
599
        "beparams": beparams,
600
        "disks": disks,
601
        "nics": nics,
602
        }
603

    
604
      (job_id, ) = _DoTests([
605
        ("/2/instances", _VerifyReturnsJob, "POST", body),
606
        ])
607

    
608
    _WaitForRapiJob(job_id)
609

    
610
    return instance
611
  except:
612
    instance.Release()
613
    raise
614

    
615

    
616
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
617
def TestRapiInstanceRemove(instance, use_client):
618
  """Test removing instance via RAPI"""
619
  if use_client:
620
    job_id = _rapi_client.DeleteInstance(instance.name)
621
  else:
622
    (job_id, ) = _DoTests([
623
      ("/2/instances/%s" % instance.name, _VerifyReturnsJob, "DELETE", None),
624
      ])
625

    
626
  _WaitForRapiJob(job_id)
627

    
628

    
629
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
630
def TestRapiInstanceMigrate(instance):
631
  """Test migrating instance via RAPI"""
632
  if not IsMigrationSupported(instance):
633
    print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
634
                              " test")
635
    return
636
  # Move to secondary node
637
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance.name))
638
  qa_utils.RunInstanceCheck(instance, True)
639
  # And back to previous primary
640
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance.name))
641

    
642

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

    
656

    
657
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
658
def TestRapiInstanceShutdown(instance):
659
  """Test stopping an instance via RAPI"""
660
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance.name))
661

    
662

    
663
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
664
def TestRapiInstanceStartup(instance):
665
  """Test starting an instance via RAPI"""
666
  _WaitForRapiJob(_rapi_client.StartupInstance(instance.name))
667

    
668

    
669
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
670
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
671
  """Test renaming instance via RAPI
672

673
  This must leave the instance with the original name (in the
674
  non-failure case).
675

676
  """
677
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
678
  qa_utils.RunInstanceCheck(rename_source, False)
679
  qa_utils.RunInstanceCheck(rename_target, False)
680
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
681
  qa_utils.RunInstanceCheck(rename_target, False)
682

    
683

    
684
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
685
def TestRapiInstanceReinstall(instance):
686
  """Test reinstalling an instance via RAPI"""
687
  if instance.disk_template == constants.DT_DISKLESS:
688
    print qa_utils.FormatInfo("Test not supported for diskless instances")
689
    return
690

    
691
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name))
692
  # By default, the instance is started again
693
  qa_utils.RunInstanceCheck(instance, True)
694

    
695
  # Reinstall again without starting
696
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance.name,
697
                                                 no_startup=True))
698

    
699

    
700
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
701
def TestRapiInstanceReplaceDisks(instance):
702
  """Test replacing instance disks via RAPI"""
703
  if not IsDiskReplacingSupported(instance):
704
    print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
705
                              " skipping test")
706
    return
707
  fn = _rapi_client.ReplaceInstanceDisks
708
  _WaitForRapiJob(fn(instance.name,
709
                     mode=constants.REPLACE_DISK_AUTO, disks=[]))
710
  _WaitForRapiJob(fn(instance.name,
711
                     mode=constants.REPLACE_DISK_SEC, disks="0"))
712

    
713

    
714
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
715
def TestRapiInstanceModify(instance):
716
  """Test modifying instance via RAPI"""
717
  default_hv = qa_config.GetDefaultHypervisor()
718

    
719
  def _ModifyInstance(**kwargs):
720
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance.name, **kwargs))
721

    
722
  _ModifyInstance(beparams={
723
    constants.BE_VCPUS: 3,
724
    })
725

    
726
  _ModifyInstance(beparams={
727
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
728
    })
729

    
730
  if default_hv == constants.HT_XEN_PVM:
731
    _ModifyInstance(hvparams={
732
      constants.HV_KERNEL_ARGS: "single",
733
      })
734
    _ModifyInstance(hvparams={
735
      constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
736
      })
737
  elif default_hv == constants.HT_XEN_HVM:
738
    _ModifyInstance(hvparams={
739
      constants.HV_BOOT_ORDER: "acn",
740
      })
741
    _ModifyInstance(hvparams={
742
      constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
743
      })
744

    
745

    
746
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
747
def TestRapiInstanceConsole(instance):
748
  """Test getting instance console information via RAPI"""
749
  result = _rapi_client.GetInstanceConsole(instance.name)
750
  console = objects.InstanceConsole.FromDict(result)
751
  AssertEqual(console.Validate(), True)
752
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance.name))
753

    
754

    
755
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
756
def TestRapiStoppedInstanceConsole(instance):
757
  """Test getting stopped instance's console information via RAPI"""
758
  try:
759
    _rapi_client.GetInstanceConsole(instance.name)
760
  except rapi.client.GanetiApiError, err:
761
    AssertEqual(err.code, 503)
762
  else:
763
    raise qa_error.Error("Getting console for stopped instance didn't"
764
                         " return HTTP 503")
765

    
766

    
767
def GetOperatingSystems():
768
  """Retrieves a list of all available operating systems.
769

770
  """
771
  return _rapi_client.GetOperatingSystems()
772

    
773

    
774
def TestInterClusterInstanceMove(src_instance, dest_instance,
775
                                 inodes, tnode):
776
  """Test tools/move-instance"""
777
  master = qa_config.GetMasterNode()
778

    
779
  rapi_pw_file = tempfile.NamedTemporaryFile()
780
  rapi_pw_file.write(_rapi_password)
781
  rapi_pw_file.flush()
782

    
783
  dest_instance.SetDiskTemplate(src_instance.disk_template)
784

    
785
  # TODO: Run some instance tests before moving back
786

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

    
818
    qa_utils.RunInstanceCheck(di, False)
819
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
820
    qa_utils.RunInstanceCheck(si, False)
821
    qa_utils.RunInstanceCheck(di, True)