Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ a77e3d33

History | View | Annotate | Download (23.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", 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
  _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
92
                                              username=username,
93
                                              password=password,
94
                                              curl_config_fn=cfg_curl)
95

    
96
  print "RAPI protocol version: %s" % _rapi_client.GetVersion()
97

    
98

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

    
106
NODE_FIELDS = ("name", "dtotal", "dfree",
107
               "mtotal", "mnode", "mfree",
108
               "pinst_cnt", "sinst_cnt", "tags")
109

    
110
GROUP_FIELDS = compat.UniqueFrozenset([
111
  "name", "uuid",
112
  "alloc_policy",
113
  "node_cnt", "node_list",
114
  ])
115

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

    
122
LIST_FIELDS = ("id", "uri")
123

    
124

    
125
def Enabled():
126
  """Return whether remote API tests should be run.
127

128
  """
129
  return qa_config.TestEnabled("rapi")
130

    
131

    
132
def _DoTests(uris):
133
  # pylint: disable=W0212
134
  # due to _SendRequest usage
135
  results = []
136

    
137
  for uri, verify, method, body in uris:
138
    assert uri.startswith("/")
139

    
140
    print "%s %s" % (method, uri)
141
    data = _rapi_client._SendRequest(method, uri, None, body)
142

    
143
    if verify is not None:
144
      if callable(verify):
145
        verify(data)
146
      else:
147
        AssertEqual(data, verify)
148

    
149
    results.append(data)
150

    
151
  return results
152

    
153

    
154
def _VerifyReturnsJob(data):
155
  if not isinstance(data, int):
156
    AssertMatch(data, r"^\d+$")
157

    
158

    
159
def TestVersion():
160
  """Testing remote API version.
161

162
  """
163
  _DoTests([
164
    ("/version", constants.RAPI_VERSION, "GET", None),
165
    ])
166

    
167

    
168
def TestEmptyCluster():
169
  """Testing remote API on an empty cluster.
170

171
  """
172
  master = qa_config.GetMasterNode()
173
  master_full = qa_utils.ResolveNodeName(master)
174

    
175
  def _VerifyInfo(data):
176
    AssertIn("name", data)
177
    AssertIn("master", data)
178
    AssertEqual(data["master"], master_full)
179

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

    
187
  def _VerifyNodesBulk(data):
188
    for node in data:
189
      for entry in NODE_FIELDS:
190
        AssertIn(entry, node)
191

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

    
199
  def _VerifyGroupsBulk(data):
200
    for group in data:
201
      for field in GROUP_FIELDS:
202
        AssertIn(field, group)
203

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

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

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

    
235

    
236
def TestRapiQuery():
237
  """Testing resource queries via remote API.
238

239
  """
240
  master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
241
  rnd = random.Random(7818)
242

    
243
  for what in constants.QR_VIA_RAPI:
244
    if what == constants.QR_JOB:
245
      namefield = "id"
246
    elif what == constants.QR_EXPORT:
247
      namefield = "export"
248
    else:
249
      namefield = "name"
250

    
251
    all_fields = query.ALL_FIELDS[what].keys()
252
    rnd.shuffle(all_fields)
253

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

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

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

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

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

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

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

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

    
305
      ("/2/query/%s?fields=%s" % (what, namefield),
306
       compat.partial(_Check, [namefield]), "GET", None),
307

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

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

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

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

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

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

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

    
360

    
361
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
362
def TestInstance(instance):
363
  """Testing getting instance(s) info via remote API.
364

365
  """
366
  def _VerifyInstance(data):
367
    for entry in INSTANCE_FIELDS:
368
      AssertIn(entry, data)
369

    
370
  def _VerifyInstancesList(data):
371
    for instance in data:
372
      for entry in LIST_FIELDS:
373
        AssertIn(entry, instance)
374

    
375
  def _VerifyInstancesBulk(data):
376
    for instance_data in data:
377
      _VerifyInstance(instance_data)
378

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

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

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

    
402

    
403
def TestNode(node):
404
  """Testing getting node(s) info via remote API.
405

406
  """
407
  def _VerifyNode(data):
408
    for entry in NODE_FIELDS:
409
      AssertIn(entry, data)
410

    
411
  def _VerifyNodesList(data):
412
    for node in data:
413
      for entry in LIST_FIELDS:
414
        AssertIn(entry, node)
415

    
416
  def _VerifyNodesBulk(data):
417
    for node_data in data:
418
      _VerifyNode(node_data)
419

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

    
426

    
427
def _FilterTags(seq):
428
  """Removes unwanted tags from a sequence.
429

430
  """
431
  ignore_re = qa_config.get("ignore-tags-re", None)
432

    
433
  if ignore_re:
434
    return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
435
  else:
436
    return seq
437

    
438

    
439
def TestTags(kind, name, tags):
440
  """Tests .../tags resources.
441

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

    
454
  def _VerifyTags(data):
455
    AssertEqual(sorted(tags), sorted(_FilterTags(data)))
456

    
457
  queryargs = "&".join("tag=%s" % i for i in tags)
458

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

    
465
  # Retrieve tags
466
  _DoTests([
467
    (uri, _VerifyTags, "GET", None),
468
    ])
469

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

    
476

    
477
def _WaitForRapiJob(job_id):
478
  """Waits for a job to finish.
479

480
  """
481
  def _VerifyJob(data):
482
    AssertEqual(data["id"], job_id)
483
    for field in JOB_FIELDS:
484
      AssertIn(field, data)
485

    
486
  _DoTests([
487
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
488
    ])
489

    
490
  return rapi.client_utils.PollJob(_rapi_client, job_id,
491
                                   cli.StdioJobPollReportCb())
492

    
493

    
494
def TestRapiNodeGroups():
495
  """Test several node group operations using RAPI.
496

497
  """
498
  (group1, group2, group3) = qa_utils.GetNonexistentGroups(3)
499

    
500
  # Create a group with no attributes
501
  body = {
502
    "name": group1,
503
    }
504

    
505
  (job_id, ) = _DoTests([
506
    ("/2/groups", _VerifyReturnsJob, "POST", body),
507
    ])
508

    
509
  _WaitForRapiJob(job_id)
510

    
511
  # Create a group specifying alloc_policy
512
  body = {
513
    "name": group2,
514
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
515
    }
516

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

    
521
  _WaitForRapiJob(job_id)
522

    
523
  # Modify alloc_policy
524
  body = {
525
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
526
    }
527

    
528
  (job_id, ) = _DoTests([
529
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
530
    ])
531

    
532
  _WaitForRapiJob(job_id)
533

    
534
  # Rename a group
535
  body = {
536
    "new_name": group3,
537
    }
538

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

    
543
  _WaitForRapiJob(job_id)
544

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

    
551
    _WaitForRapiJob(job_id)
552

    
553

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

    
567
    beparams = {
568
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
569
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
570
      }
571

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

    
593
      (job_id, ) = _DoTests([
594
        ("/2/instances", _VerifyReturnsJob, "POST", body),
595
        ])
596

    
597
    _WaitForRapiJob(job_id)
598

    
599
    return instance
600
  except:
601
    qa_config.ReleaseInstance(instance)
602
    raise
603

    
604

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

    
615
  _WaitForRapiJob(job_id)
616

    
617

    
618
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
619
def TestRapiInstanceMigrate(instance):
620
  """Test migrating instance via RAPI"""
621
  if not IsMigrationSupported(instance):
622
    print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
623
                              " test")
624
    return
625
  # Move to secondary node
626
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
627
  qa_utils.RunInstanceCheck(instance, True)
628
  # And back to previous primary
629
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
630

    
631

    
632
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
633
def TestRapiInstanceFailover(instance):
634
  """Test failing over instance via RAPI"""
635
  if not IsFailoverSupported(instance):
636
    print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
637
                              " test")
638
    return
639
  # Move to secondary node
640
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
641
  qa_utils.RunInstanceCheck(instance, True)
642
  # And back to previous primary
643
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
644

    
645

    
646
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
647
def TestRapiInstanceShutdown(instance):
648
  """Test stopping an instance via RAPI"""
649
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
650

    
651

    
652
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
653
def TestRapiInstanceStartup(instance):
654
  """Test starting an instance via RAPI"""
655
  _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
656

    
657

    
658
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
659
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
660
  """Test renaming instance via RAPI
661

662
  This must leave the instance with the original name (in the
663
  non-failure case).
664

665
  """
666
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
667
  qa_utils.RunInstanceCheck(rename_source, False)
668
  qa_utils.RunInstanceCheck(rename_target, False)
669
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
670
  qa_utils.RunInstanceCheck(rename_target, False)
671

    
672

    
673
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
674
def TestRapiInstanceReinstall(instance):
675
  """Test reinstalling an instance via RAPI"""
676
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
677
  # By default, the instance is started again
678
  qa_utils.RunInstanceCheck(instance, True)
679

    
680
  # Reinstall again without starting
681
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"],
682
                                                 no_startup=True))
683

    
684

    
685
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
686
def TestRapiInstanceReplaceDisks(instance):
687
  """Test replacing instance disks via RAPI"""
688
  if not IsDiskReplacingSupported(instance):
689
    print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
690
                              " skipping test")
691
    return
692
  fn = _rapi_client.ReplaceInstanceDisks
693
  _WaitForRapiJob(fn(instance["name"],
694
                     mode=constants.REPLACE_DISK_AUTO, disks=[]))
695
  _WaitForRapiJob(fn(instance["name"],
696
                     mode=constants.REPLACE_DISK_SEC, disks="0"))
697

    
698

    
699
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
700
def TestRapiInstanceModify(instance):
701
  """Test modifying instance via RAPI"""
702
  default_hv = qa_config.GetDefaultHypervisor()
703

    
704
  def _ModifyInstance(**kwargs):
705
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
706

    
707
  _ModifyInstance(beparams={
708
    constants.BE_VCPUS: 3,
709
    })
710

    
711
  _ModifyInstance(beparams={
712
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
713
    })
714

    
715
  if default_hv == constants.HT_XEN_PVM:
716
    _ModifyInstance(hvparams={
717
      constants.HV_KERNEL_ARGS: "single",
718
      })
719
    _ModifyInstance(hvparams={
720
      constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
721
      })
722
  elif default_hv == constants.HT_XEN_HVM:
723
    _ModifyInstance(hvparams={
724
      constants.HV_BOOT_ORDER: "acn",
725
      })
726
    _ModifyInstance(hvparams={
727
      constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
728
      })
729

    
730

    
731
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
732
def TestRapiInstanceConsole(instance):
733
  """Test getting instance console information via RAPI"""
734
  result = _rapi_client.GetInstanceConsole(instance["name"])
735
  console = objects.InstanceConsole.FromDict(result)
736
  AssertEqual(console.Validate(), True)
737
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
738

    
739

    
740
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
741
def TestRapiStoppedInstanceConsole(instance):
742
  """Test getting stopped instance's console information via RAPI"""
743
  try:
744
    _rapi_client.GetInstanceConsole(instance["name"])
745
  except rapi.client.GanetiApiError, err:
746
    AssertEqual(err.code, 503)
747
  else:
748
    raise qa_error.Error("Getting console for stopped instance didn't"
749
                         " return HTTP 503")
750

    
751

    
752
def GetOperatingSystems():
753
  """Retrieves a list of all available operating systems.
754

755
  """
756
  return _rapi_client.GetOperatingSystems()
757

    
758

    
759
def TestInterClusterInstanceMove(src_instance, dest_instance,
760
                                 inodes, tnode):
761
  """Test tools/move-instance"""
762
  master = qa_config.GetMasterNode()
763

    
764
  rapi_pw_file = tempfile.NamedTemporaryFile()
765
  rapi_pw_file.write(_rapi_password)
766
  rapi_pw_file.flush()
767

    
768
  qa_config.SetInstanceTemplate(dest_instance,
769
                                qa_config.GetInstanceTemplate(src_instance))
770

    
771
  # TODO: Run some instance tests before moving back
772

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

    
804
    qa_utils.RunInstanceCheck(di, False)
805
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
806
    qa_utils.RunInstanceCheck(si, False)
807
    qa_utils.RunInstanceCheck(di, True)