Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 51131cad

History | View | Annotate | Download (21.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 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

    
29
from ganeti import utils
30
from ganeti import constants
31
from ganeti import errors
32
from ganeti import cli
33
from ganeti import rapi
34
from ganeti import objects
35
from ganeti import query
36
from ganeti import compat
37
from ganeti import qlang
38

    
39
import ganeti.rapi.client        # pylint: disable=W0611
40
import ganeti.rapi.client_utils
41

    
42
import qa_config
43
import qa_utils
44
import qa_error
45

    
46
from qa_utils import (AssertEqual, AssertIn, AssertMatch, StartLocalCommand)
47
from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG
48

    
49

    
50
_rapi_ca = None
51
_rapi_client = None
52
_rapi_username = None
53
_rapi_password = None
54

    
55

    
56
def Setup(username, password):
57
  """Configures the RAPI client.
58

59
  """
60
  # pylint: disable=W0603
61
  # due to global usage
62
  global _rapi_ca
63
  global _rapi_client
64
  global _rapi_username
65
  global _rapi_password
66

    
67
  _rapi_username = username
68
  _rapi_password = password
69

    
70
  master = qa_config.GetMasterNode()
71

    
72
  # Load RAPI certificate from master node
73
  cmd = ["cat", constants.RAPI_CERT_FILE]
74

    
75
  # Write to temporary file
76
  _rapi_ca = tempfile.NamedTemporaryFile()
77
  _rapi_ca.write(qa_utils.GetCommandOutput(master["primary"],
78
                                           utils.ShellQuoteArgs(cmd)))
79
  _rapi_ca.flush()
80

    
81
  port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
82
  cfg_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
83
                                           proxy="")
84

    
85
  _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
86
                                              username=username,
87
                                              password=password,
88
                                              curl_config_fn=cfg_curl)
89

    
90
  print "RAPI protocol version: %s" % _rapi_client.GetVersion()
91

    
92

    
93
INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
94
                   "admin_state",
95
                   "disk_template", "disk.sizes",
96
                   "nic.ips", "nic.macs", "nic.modes", "nic.links",
97
                   "beparams", "hvparams",
98
                   "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
99

    
100
NODE_FIELDS = ("name", "dtotal", "dfree",
101
               "mtotal", "mnode", "mfree",
102
               "pinst_cnt", "sinst_cnt", "tags")
103

    
104
GROUP_FIELDS = frozenset([
105
  "name", "uuid",
106
  "alloc_policy",
107
  "node_cnt", "node_list",
108
  ])
109

    
110
JOB_FIELDS = frozenset([
111
  "id", "ops", "status", "summary",
112
  "opstatus", "opresult", "oplog",
113
  "received_ts", "start_ts", "end_ts",
114
  ])
115

    
116
LIST_FIELDS = ("id", "uri")
117

    
118

    
119
def Enabled():
120
  """Return whether remote API tests should be run.
121

122
  """
123
  return qa_config.TestEnabled("rapi")
124

    
125

    
126
def _DoTests(uris):
127
  # pylint: disable=W0212
128
  # due to _SendRequest usage
129
  results = []
130

    
131
  for uri, verify, method, body in uris:
132
    assert uri.startswith("/")
133

    
134
    print "%s %s" % (method, uri)
135
    data = _rapi_client._SendRequest(method, uri, None, body)
136

    
137
    if verify is not None:
138
      if callable(verify):
139
        verify(data)
140
      else:
141
        AssertEqual(data, verify)
142

    
143
    results.append(data)
144

    
145
  return results
146

    
147

    
148
def _VerifyReturnsJob(data):
149
  AssertMatch(data, r"^\d+$")
150

    
151

    
152
def TestVersion():
153
  """Testing remote API version.
154

155
  """
156
  _DoTests([
157
    ("/version", constants.RAPI_VERSION, "GET", None),
158
    ])
159

    
160

    
161
def TestEmptyCluster():
162
  """Testing remote API on an empty cluster.
163

164
  """
165
  master = qa_config.GetMasterNode()
166
  master_full = qa_utils.ResolveNodeName(master)
167

    
168
  def _VerifyInfo(data):
169
    AssertIn("name", data)
170
    AssertIn("master", data)
171
    AssertEqual(data["master"], master_full)
172

    
173
  def _VerifyNodes(data):
174
    master_entry = {
175
      "id": master_full,
176
      "uri": "/2/nodes/%s" % master_full,
177
      }
178
    AssertIn(master_entry, data)
179

    
180
  def _VerifyNodesBulk(data):
181
    for node in data:
182
      for entry in NODE_FIELDS:
183
        AssertIn(entry, node)
184

    
185
  def _VerifyGroups(data):
186
    default_group = {
187
      "name": constants.INITIAL_NODE_GROUP_NAME,
188
      "uri": "/2/groups/" + constants.INITIAL_NODE_GROUP_NAME,
189
      }
190
    AssertIn(default_group, data)
191

    
192
  def _VerifyGroupsBulk(data):
193
    for group in data:
194
      for field in GROUP_FIELDS:
195
        AssertIn(field, group)
196

    
197
  _DoTests([
198
    ("/", None, "GET", None),
199
    ("/2/info", _VerifyInfo, "GET", None),
200
    ("/2/tags", None, "GET", None),
201
    ("/2/nodes", _VerifyNodes, "GET", None),
202
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
203
    ("/2/groups", _VerifyGroups, "GET", None),
204
    ("/2/groups?bulk=1", _VerifyGroupsBulk, "GET", None),
205
    ("/2/instances", [], "GET", None),
206
    ("/2/instances?bulk=1", [], "GET", None),
207
    ("/2/os", None, "GET", None),
208
    ])
209

    
210
  # Test HTTP Not Found
211
  for method in ["GET", "PUT", "POST", "DELETE"]:
212
    try:
213
      _DoTests([("/99/resource/not/here/99", None, method, None)])
214
    except rapi.client.GanetiApiError, err:
215
      AssertEqual(err.code, 404)
216
    else:
217
      raise qa_error.Error("Non-existent resource didn't return HTTP 404")
218

    
219
  # Test HTTP Not Implemented
220
  for method in ["PUT", "POST", "DELETE"]:
221
    try:
222
      _DoTests([("/version", None, method, None)])
223
    except rapi.client.GanetiApiError, err:
224
      AssertEqual(err.code, 501)
225
    else:
226
      raise qa_error.Error("Non-implemented method didn't fail")
227

    
228

    
229
def TestRapiQuery():
230
  """Testing resource queries via remote API.
231

232
  """
233
  master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
234
  rnd = random.Random(7818)
235

    
236
  for what in constants.QR_VIA_RAPI:
237
    if what == constants.QR_JOB:
238
      namefield = "id"
239
    elif what == constants.QR_EXPORT:
240
      namefield = "export"
241
    else:
242
      namefield = "name"
243

    
244
    all_fields = query.ALL_FIELDS[what].keys()
245
    rnd.shuffle(all_fields)
246

    
247
    # No fields, should return everything
248
    result = _rapi_client.QueryFields(what)
249
    qresult = objects.QueryFieldsResponse.FromDict(result)
250
    AssertEqual(len(qresult.fields), len(all_fields))
251

    
252
    # One field
253
    result = _rapi_client.QueryFields(what, fields=[namefield])
254
    qresult = objects.QueryFieldsResponse.FromDict(result)
255
    AssertEqual(len(qresult.fields), 1)
256

    
257
    # Specify all fields, order must be correct
258
    result = _rapi_client.QueryFields(what, fields=all_fields)
259
    qresult = objects.QueryFieldsResponse.FromDict(result)
260
    AssertEqual(len(qresult.fields), len(all_fields))
261
    AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
262

    
263
    # Unknown field
264
    result = _rapi_client.QueryFields(what, fields=["_unknown!"])
265
    qresult = objects.QueryFieldsResponse.FromDict(result)
266
    AssertEqual(len(qresult.fields), 1)
267
    AssertEqual(qresult.fields[0].name, "_unknown!")
268
    AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
269

    
270
    # Try once more, this time without the client
271
    _DoTests([
272
      ("/2/query/%s/fields" % what, None, "GET", None),
273
      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
274
       None, "GET", None),
275
      ])
276

    
277
    # Try missing query argument
278
    try:
279
      _DoTests([
280
        ("/2/query/%s" % what, None, "GET", None),
281
        ])
282
    except rapi.client.GanetiApiError, err:
283
      AssertEqual(err.code, 400)
284
    else:
285
      raise qa_error.Error("Request missing 'fields' parameter didn't fail")
286

    
287
    def _Check(exp_fields, data):
288
      qresult = objects.QueryResponse.FromDict(data)
289
      AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
290
      if not isinstance(qresult.data, list):
291
        raise qa_error.Error("Query did not return a list")
292

    
293
    _DoTests([
294
      # Specify fields in query
295
      ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
296
       compat.partial(_Check, all_fields), "GET", None),
297

    
298
      ("/2/query/%s?fields=%s" % (what, namefield),
299
       compat.partial(_Check, [namefield]), "GET", None),
300

    
301
      # Note the spaces
302
      ("/2/query/%s?fields=%s,%%20%s%%09,%s%%20" %
303
       (what, namefield, namefield, namefield),
304
       compat.partial(_Check, [namefield] * 3), "GET", None),
305

    
306
      # PUT with fields in query
307
      ("/2/query/%s?fields=%s" % (what, namefield),
308
       compat.partial(_Check, [namefield]), "PUT", {}),
309

    
310
      # Fields in body
311
      ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
312
         "fields": all_fields,
313
         }),
314

    
315
      ("/2/query/%s" % what, compat.partial(_Check, [namefield] * 4), "PUT", {
316
         "fields": [namefield] * 4,
317
         }),
318
      ])
319

    
320
    def _CheckFilter():
321
      _DoTests([
322
        # With filter
323
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
324
           "fields": all_fields,
325
           "filter": [qlang.OP_TRUE, namefield],
326
           }),
327
        ])
328

    
329
    if what == constants.QR_LOCK:
330
      # Locks can't be filtered
331
      try:
332
        _CheckFilter()
333
      except rapi.client.GanetiApiError, err:
334
        AssertEqual(err.code, 500)
335
      else:
336
        raise qa_error.Error("Filtering locks didn't fail")
337
    else:
338
      _CheckFilter()
339

    
340
    if what == constants.QR_NODE:
341
      # Test with filter
342
      (nodes, ) = _DoTests([("/2/query/%s" % what,
343
        compat.partial(_Check, ["name", "master"]), "PUT", {
344
        "fields": ["name", "master"],
345
        "filter": [qlang.OP_TRUE, "master"],
346
        })])
347
      qresult = objects.QueryResponse.FromDict(nodes)
348
      AssertEqual(qresult.data, [
349
        [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
350
        ])
351

    
352

    
353
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
354
def TestInstance(instance):
355
  """Testing getting instance(s) info via remote API.
356

357
  """
358
  def _VerifyInstance(data):
359
    for entry in INSTANCE_FIELDS:
360
      AssertIn(entry, data)
361

    
362
  def _VerifyInstancesList(data):
363
    for instance in data:
364
      for entry in LIST_FIELDS:
365
        AssertIn(entry, instance)
366

    
367
  def _VerifyInstancesBulk(data):
368
    for instance_data in data:
369
      _VerifyInstance(instance_data)
370

    
371
  _DoTests([
372
    ("/2/instances/%s" % instance["name"], _VerifyInstance, "GET", None),
373
    ("/2/instances", _VerifyInstancesList, "GET", None),
374
    ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
375
    ("/2/instances/%s/activate-disks" % instance["name"],
376
     _VerifyReturnsJob, "PUT", None),
377
    ("/2/instances/%s/deactivate-disks" % instance["name"],
378
     _VerifyReturnsJob, "PUT", None),
379
    ])
380

    
381
  # Test OpBackupPrepare
382
  (job_id, ) = _DoTests([
383
    ("/2/instances/%s/prepare-export?mode=%s" %
384
     (instance["name"], constants.EXPORT_MODE_REMOTE),
385
     _VerifyReturnsJob, "PUT", None),
386
    ])
387

    
388
  result = _WaitForRapiJob(job_id)[0]
389
  AssertEqual(len(result["handshake"]), 3)
390
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
391
  AssertEqual(len(result["x509_key_name"]), 3)
392
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
393

    
394

    
395
def TestNode(node):
396
  """Testing getting node(s) info via remote API.
397

398
  """
399
  def _VerifyNode(data):
400
    for entry in NODE_FIELDS:
401
      AssertIn(entry, data)
402

    
403
  def _VerifyNodesList(data):
404
    for node in data:
405
      for entry in LIST_FIELDS:
406
        AssertIn(entry, node)
407

    
408
  def _VerifyNodesBulk(data):
409
    for node_data in data:
410
      _VerifyNode(node_data)
411

    
412
  _DoTests([
413
    ("/2/nodes/%s" % node["primary"], _VerifyNode, "GET", None),
414
    ("/2/nodes", _VerifyNodesList, "GET", None),
415
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
416
    ])
417

    
418

    
419
def TestTags(kind, name, tags):
420
  """Tests .../tags resources.
421

422
  """
423
  if kind == constants.TAG_CLUSTER:
424
    uri = "/2/tags"
425
  elif kind == constants.TAG_NODE:
426
    uri = "/2/nodes/%s/tags" % name
427
  elif kind == constants.TAG_INSTANCE:
428
    uri = "/2/instances/%s/tags" % name
429
  elif kind == constants.TAG_NODEGROUP:
430
    uri = "/2/groups/%s/tags" % name
431
  else:
432
    raise errors.ProgrammerError("Unknown tag kind")
433

    
434
  def _VerifyTags(data):
435
    AssertEqual(sorted(tags), sorted(data))
436

    
437
  queryargs = "&".join("tag=%s" % i for i in tags)
438

    
439
  # Add tags
440
  (job_id, ) = _DoTests([
441
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
442
    ])
443
  _WaitForRapiJob(job_id)
444

    
445
  # Retrieve tags
446
  _DoTests([
447
    (uri, _VerifyTags, "GET", None),
448
    ])
449

    
450
  # Remove tags
451
  (job_id, ) = _DoTests([
452
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "DELETE", None),
453
    ])
454
  _WaitForRapiJob(job_id)
455

    
456

    
457
def _WaitForRapiJob(job_id):
458
  """Waits for a job to finish.
459

460
  """
461
  def _VerifyJob(data):
462
    AssertEqual(data["id"], job_id)
463
    for field in JOB_FIELDS:
464
      AssertIn(field, data)
465

    
466
  _DoTests([
467
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
468
    ])
469

    
470
  return rapi.client_utils.PollJob(_rapi_client, job_id,
471
                                   cli.StdioJobPollReportCb())
472

    
473

    
474
def TestRapiNodeGroups():
475
  """Test several node group operations using RAPI.
476

477
  """
478
  groups = qa_config.get("groups", {})
479
  group1, group2, group3 = groups.get("inexistent-groups",
480
                                      ["group1", "group2", "group3"])[:3]
481

    
482
  # Create a group with no attributes
483
  body = {
484
    "name": group1,
485
    }
486

    
487
  (job_id, ) = _DoTests([
488
    ("/2/groups", _VerifyReturnsJob, "POST", body),
489
    ])
490

    
491
  _WaitForRapiJob(job_id)
492

    
493
  # Create a group specifying alloc_policy
494
  body = {
495
    "name": group2,
496
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
497
    }
498

    
499
  (job_id, ) = _DoTests([
500
    ("/2/groups", _VerifyReturnsJob, "POST", body),
501
    ])
502

    
503
  _WaitForRapiJob(job_id)
504

    
505
  # Modify alloc_policy
506
  body = {
507
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
508
    }
509

    
510
  (job_id, ) = _DoTests([
511
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
512
    ])
513

    
514
  _WaitForRapiJob(job_id)
515

    
516
  # Rename a group
517
  body = {
518
    "new_name": group3,
519
    }
520

    
521
  (job_id, ) = _DoTests([
522
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
523
    ])
524

    
525
  _WaitForRapiJob(job_id)
526

    
527
  # Delete groups
528
  for group in [group1, group3]:
529
    (job_id, ) = _DoTests([
530
      ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
531
      ])
532

    
533
    _WaitForRapiJob(job_id)
534

    
535

    
536
def TestRapiInstanceAdd(node, use_client):
537
  """Test adding a new instance via RAPI"""
538
  instance = qa_config.AcquireInstance()
539
  try:
540
    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
541
    disks = [{"size": size} for size in disk_sizes]
542
    nics = [{}]
543

    
544
    beparams = {
545
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
546
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
547
      }
548

    
549
    if use_client:
550
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
551
                                           instance["name"],
552
                                           constants.DT_PLAIN,
553
                                           disks, nics,
554
                                           os=qa_config.get("os"),
555
                                           pnode=node["primary"],
556
                                           beparams=beparams)
557
    else:
558
      body = {
559
        "__version__": 1,
560
        "mode": constants.INSTANCE_CREATE,
561
        "name": instance["name"],
562
        "os_type": qa_config.get("os"),
563
        "disk_template": constants.DT_PLAIN,
564
        "pnode": node["primary"],
565
        "beparams": beparams,
566
        "disks": disks,
567
        "nics": nics,
568
        }
569

    
570
      (job_id, ) = _DoTests([
571
        ("/2/instances", _VerifyReturnsJob, "POST", body),
572
        ])
573

    
574
    _WaitForRapiJob(job_id)
575

    
576
    return instance
577
  except:
578
    qa_config.ReleaseInstance(instance)
579
    raise
580

    
581

    
582
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
583
def TestRapiInstanceRemove(instance, use_client):
584
  """Test removing instance via RAPI"""
585
  if use_client:
586
    job_id = _rapi_client.DeleteInstance(instance["name"])
587
  else:
588
    (job_id, ) = _DoTests([
589
      ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
590
      ])
591

    
592
  _WaitForRapiJob(job_id)
593

    
594
  qa_config.ReleaseInstance(instance)
595

    
596

    
597
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
598
def TestRapiInstanceMigrate(instance):
599
  """Test migrating instance via RAPI"""
600
  # Move to secondary node
601
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
602
  qa_utils.RunInstanceCheck(instance, True)
603
  # And back to previous primary
604
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
605

    
606

    
607
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
608
def TestRapiInstanceFailover(instance):
609
  """Test failing over instance via RAPI"""
610
  # Move to secondary node
611
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
612
  qa_utils.RunInstanceCheck(instance, True)
613
  # And back to previous primary
614
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
615

    
616

    
617
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
618
def TestRapiInstanceShutdown(instance):
619
  """Test stopping an instance via RAPI"""
620
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
621

    
622

    
623
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
624
def TestRapiInstanceStartup(instance):
625
  """Test starting an instance via RAPI"""
626
  _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
627

    
628

    
629
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
630
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
631
  """Test renaming instance via RAPI
632

633
  This must leave the instance with the original name (in the
634
  non-failure case).
635

636
  """
637
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
638
  qa_utils.RunInstanceCheck(rename_source, False)
639
  qa_utils.RunInstanceCheck(rename_target, False)
640
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
641
  qa_utils.RunInstanceCheck(rename_target, False)
642

    
643

    
644
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
645
def TestRapiInstanceReinstall(instance):
646
  """Test reinstalling an instance via RAPI"""
647
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
648
  # By default, the instance is started again
649
  qa_utils.RunInstanceCheck(instance, True)
650

    
651
  # Reinstall again without starting
652
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"],
653
                                                 no_startup=True))
654

    
655

    
656
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
657
def TestRapiInstanceReplaceDisks(instance):
658
  """Test replacing instance disks via RAPI"""
659
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
660
    mode=constants.REPLACE_DISK_AUTO, disks=[]))
661
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
662
    mode=constants.REPLACE_DISK_SEC, disks="0"))
663

    
664

    
665
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
666
def TestRapiInstanceModify(instance):
667
  """Test modifying instance via RAPI"""
668
  def _ModifyInstance(**kwargs):
669
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
670

    
671
  _ModifyInstance(hvparams={
672
    constants.HV_KERNEL_ARGS: "single",
673
    })
674

    
675
  _ModifyInstance(beparams={
676
    constants.BE_VCPUS: 3,
677
    })
678

    
679
  _ModifyInstance(beparams={
680
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
681
    })
682

    
683
  _ModifyInstance(hvparams={
684
    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
685
    })
686

    
687

    
688
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
689
def TestRapiInstanceConsole(instance):
690
  """Test getting instance console information via RAPI"""
691
  result = _rapi_client.GetInstanceConsole(instance["name"])
692
  console = objects.InstanceConsole.FromDict(result)
693
  AssertEqual(console.Validate(), True)
694
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
695

    
696

    
697
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
698
def TestRapiStoppedInstanceConsole(instance):
699
  """Test getting stopped instance's console information via RAPI"""
700
  try:
701
    _rapi_client.GetInstanceConsole(instance["name"])
702
  except rapi.client.GanetiApiError, err:
703
    AssertEqual(err.code, 503)
704
  else:
705
    raise qa_error.Error("Getting console for stopped instance didn't"
706
                         " return HTTP 503")
707

    
708

    
709
def GetOperatingSystems():
710
  """Retrieves a list of all available operating systems.
711

712
  """
713
  return _rapi_client.GetOperatingSystems()
714

    
715

    
716
def TestInterClusterInstanceMove(src_instance, dest_instance,
717
                                 pnode, snode, tnode):
718
  """Test tools/move-instance"""
719
  master = qa_config.GetMasterNode()
720

    
721
  rapi_pw_file = tempfile.NamedTemporaryFile()
722
  rapi_pw_file.write(_rapi_password)
723
  rapi_pw_file.flush()
724

    
725
  # TODO: Run some instance tests before moving back
726

    
727
  if snode is None:
728
    # instance is not redundant, but we still need to pass a node
729
    # (which will be ignored)
730
    fsec = tnode
731
  else:
732
    fsec = snode
733
  # note: pnode:snode are the *current* nodes, so we move it first to
734
  # tnode:pnode, then back to pnode:snode
735
  for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
736
                          tnode["primary"], pnode["primary"]),
737
                         (dest_instance["name"], src_instance["name"],
738
                          pnode["primary"], fsec["primary"])]:
739
    cmd = [
740
      "../tools/move-instance",
741
      "--verbose",
742
      "--src-ca-file=%s" % _rapi_ca.name,
743
      "--src-username=%s" % _rapi_username,
744
      "--src-password-file=%s" % rapi_pw_file.name,
745
      "--dest-instance-name=%s" % di,
746
      "--dest-primary-node=%s" % pn,
747
      "--dest-secondary-node=%s" % sn,
748
      "--net=0:mac=%s" % constants.VALUE_GENERATE,
749
      master["primary"],
750
      master["primary"],
751
      si,
752
      ]
753

    
754
    qa_utils.RunInstanceCheck(di, False)
755
    AssertEqual(StartLocalCommand(cmd).wait(), 0)
756
    qa_utils.RunInstanceCheck(si, False)
757
    qa_utils.RunInstanceCheck(di, True)