Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 496d5ac8

History | View | Annotate | Download (20.2 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

    
48

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

    
54

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

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

    
66
  _rapi_username = username
67
  _rapi_password = password
68

    
69
  master = qa_config.GetMasterNode()
70

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

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

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

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

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

    
91

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

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

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

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

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

    
117

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

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

    
124

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

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

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

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

    
142
    results.append(data)
143

    
144
  return results
145

    
146

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

    
150

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

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

    
159

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

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

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

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

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

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

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

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

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

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

    
227

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
351

    
352
def TestInstance(instance):
353
  """Testing getting instance(s) info via remote API.
354

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

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

    
365
  def _VerifyInstancesBulk(data):
366
    for instance_data in data:
367
      _VerifyInstance(instance_data)
368

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

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

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

    
392

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

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

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

    
406
  def _VerifyNodesBulk(data):
407
    for node_data in data:
408
      _VerifyNode(node_data)
409

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

    
416

    
417
def TestTags(kind, name, tags):
418
  """Tests .../tags resources.
419

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

    
432
  def _VerifyTags(data):
433
    AssertEqual(sorted(tags), sorted(data))
434

    
435
  queryargs = "&".join("tag=%s" % i for i in tags)
436

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

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

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

    
454

    
455
def _WaitForRapiJob(job_id):
456
  """Waits for a job to finish.
457

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

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

    
468
  return rapi.client_utils.PollJob(_rapi_client, job_id,
469
                                   cli.StdioJobPollReportCb())
470

    
471

    
472
def TestRapiNodeGroups():
473
  """Test several node group operations using RAPI.
474

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

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

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

    
489
  _WaitForRapiJob(job_id)
490

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

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

    
501
  _WaitForRapiJob(job_id)
502

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

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

    
512
  _WaitForRapiJob(job_id)
513

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

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

    
523
  _WaitForRapiJob(job_id)
524

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

    
531
    _WaitForRapiJob(job_id)
532

    
533

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

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

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

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

    
572
    _WaitForRapiJob(job_id)
573

    
574
    return instance
575
  except:
576
    qa_config.ReleaseInstance(instance)
577
    raise
578

    
579

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

    
589
  _WaitForRapiJob(job_id)
590

    
591
  qa_config.ReleaseInstance(instance)
592

    
593

    
594
def TestRapiInstanceMigrate(instance):
595
  """Test migrating instance via RAPI"""
596
  # Move to secondary node
597
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
598
  # And back to previous primary
599
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
600

    
601

    
602
def TestRapiInstanceFailover(instance):
603
  """Test failing over instance via RAPI"""
604
  # Move to secondary node
605
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
606
  # And back to previous primary
607
  _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
608

    
609

    
610
def TestRapiInstanceShutdown(instance):
611
  """Test stopping an instance via RAPI"""
612
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
613

    
614

    
615
def TestRapiInstanceStartup(instance):
616
  """Test starting an instance via RAPI"""
617
  _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
618

    
619

    
620
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
621
  """Test renaming instance via RAPI
622

623
  This must leave the instance with the original name (in the
624
  non-failure case).
625

626
  """
627
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
628
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
629

    
630

    
631
def TestRapiInstanceReinstall(instance):
632
  """Test reinstalling an instance via RAPI"""
633
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
634

    
635

    
636
def TestRapiInstanceReplaceDisks(instance):
637
  """Test replacing instance disks via RAPI"""
638
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
639
    mode=constants.REPLACE_DISK_AUTO, disks=[]))
640
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
641
    mode=constants.REPLACE_DISK_SEC, disks="0"))
642

    
643

    
644
def TestRapiInstanceModify(instance):
645
  """Test modifying instance via RAPI"""
646
  def _ModifyInstance(**kwargs):
647
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
648

    
649
  _ModifyInstance(hvparams={
650
    constants.HV_KERNEL_ARGS: "single",
651
    })
652

    
653
  _ModifyInstance(beparams={
654
    constants.BE_VCPUS: 3,
655
    })
656

    
657
  _ModifyInstance(beparams={
658
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
659
    })
660

    
661
  _ModifyInstance(hvparams={
662
    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
663
    })
664

    
665

    
666
def TestRapiInstanceConsole(instance):
667
  """Test getting instance console information via RAPI"""
668
  result = _rapi_client.GetInstanceConsole(instance["name"])
669
  console = objects.InstanceConsole.FromDict(result)
670
  AssertEqual(console.Validate(), True)
671
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
672

    
673

    
674
def TestRapiStoppedInstanceConsole(instance):
675
  """Test getting stopped instance's console information via RAPI"""
676
  try:
677
    _rapi_client.GetInstanceConsole(instance["name"])
678
  except rapi.client.GanetiApiError, err:
679
    AssertEqual(err.code, 503)
680
  else:
681
    raise qa_error.Error("Getting console for stopped instance didn't"
682
                         " return HTTP 503")
683

    
684

    
685
def GetOperatingSystems():
686
  """Retrieves a list of all available operating systems.
687

688
  """
689
  return _rapi_client.GetOperatingSystems()
690

    
691

    
692
def TestInterClusterInstanceMove(src_instance, dest_instance,
693
                                 pnode, snode, tnode):
694
  """Test tools/move-instance"""
695
  master = qa_config.GetMasterNode()
696

    
697
  rapi_pw_file = tempfile.NamedTemporaryFile()
698
  rapi_pw_file.write(_rapi_password)
699
  rapi_pw_file.flush()
700

    
701
  # TODO: Run some instance tests before moving back
702

    
703
  if snode is None:
704
    # instance is not redundant, but we still need to pass a node
705
    # (which will be ignored)
706
    fsec = tnode
707
  else:
708
    fsec = snode
709
  # note: pnode:snode are the *current* nodes, so we move it first to
710
  # tnode:pnode, then back to pnode:snode
711
  for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
712
                          tnode["primary"], pnode["primary"]),
713
                         (dest_instance["name"], src_instance["name"],
714
                          pnode["primary"], fsec["primary"])]:
715
    cmd = [
716
      "../tools/move-instance",
717
      "--verbose",
718
      "--src-ca-file=%s" % _rapi_ca.name,
719
      "--src-username=%s" % _rapi_username,
720
      "--src-password-file=%s" % rapi_pw_file.name,
721
      "--dest-instance-name=%s" % di,
722
      "--dest-primary-node=%s" % pn,
723
      "--dest-secondary-node=%s" % sn,
724
      "--net=0:mac=%s" % constants.VALUE_GENERATE,
725
      master["primary"],
726
      master["primary"],
727
      si,
728
      ]
729

    
730
    AssertEqual(StartLocalCommand(cmd).wait(), 0)