Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 0afce24e

History | View | Annotate | Download (22.9 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 = instance.GetNicMacAddr(0, constants.VALUE_GENERATE)
562
    nics = [{
563
      constants.INIC_MAC: nic0_mac,
564
      }]
565

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

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

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

    
596
    _WaitForRapiJob(job_id)
597

    
598
    return instance
599
  except:
600
    instance.Release()
601
    raise
602

    
603

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

    
614
  _WaitForRapiJob(job_id)
615

    
616

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

    
630

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

    
644

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

    
650

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

    
656

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

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

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

    
671

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

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

    
683

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

    
697

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

    
703
  def _ModifyInstance(**kwargs):
704
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance.name, **kwargs))
705

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

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

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

    
729

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

    
738

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

    
750

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

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

    
757

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

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

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

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

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

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