Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 4c1a464b

History | View | Annotate | Download (19.9 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
    all_fields = query.ALL_FIELDS[what].keys()
237
    rnd.shuffle(all_fields)
238

    
239
    # No fields, should return everything
240
    result = _rapi_client.QueryFields(what)
241
    qresult = objects.QueryFieldsResponse.FromDict(result)
242
    AssertEqual(len(qresult.fields), len(all_fields))
243

    
244
    # One field
245
    result = _rapi_client.QueryFields(what, fields=["name"])
246
    qresult = objects.QueryFieldsResponse.FromDict(result)
247
    AssertEqual(len(qresult.fields), 1)
248

    
249
    # Specify all fields, order must be correct
250
    result = _rapi_client.QueryFields(what, fields=all_fields)
251
    qresult = objects.QueryFieldsResponse.FromDict(result)
252
    AssertEqual(len(qresult.fields), len(all_fields))
253
    AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
254

    
255
    # Unknown field
256
    result = _rapi_client.QueryFields(what, fields=["_unknown!"])
257
    qresult = objects.QueryFieldsResponse.FromDict(result)
258
    AssertEqual(len(qresult.fields), 1)
259
    AssertEqual(qresult.fields[0].name, "_unknown!")
260
    AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
261

    
262
    # Try once more, this time without the client
263
    _DoTests([
264
      ("/2/query/%s/fields" % what, None, "GET", None),
265
      ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
266
       None, "GET", None),
267
      ])
268

    
269
    # Try missing query argument
270
    try:
271
      _DoTests([
272
        ("/2/query/%s" % what, None, "GET", None),
273
        ])
274
    except rapi.client.GanetiApiError, err:
275
      AssertEqual(err.code, 400)
276
    else:
277
      raise qa_error.Error("Request missing 'fields' parameter didn't fail")
278

    
279
    def _Check(exp_fields, data):
280
      qresult = objects.QueryResponse.FromDict(data)
281
      AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
282
      if not isinstance(qresult.data, list):
283
        raise qa_error.Error("Query did not return a list")
284

    
285
    _DoTests([
286
      # Specify fields in query
287
      ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
288
       compat.partial(_Check, all_fields), "GET", None),
289

    
290
      ("/2/query/%s?fields=name" % what,
291
       compat.partial(_Check, ["name"]), "GET", None),
292

    
293
      # Note the spaces
294
      ("/2/query/%s?fields=name,%%20name%%09,name%%20" % what,
295
       compat.partial(_Check, ["name"] * 3), "GET", None),
296

    
297
      # PUT with fields in query
298
      ("/2/query/%s?fields=name" % what,
299
       compat.partial(_Check, ["name"]), "PUT", {}),
300

    
301
      # Fields in body
302
      ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
303
         "fields": all_fields,
304
         }),
305

    
306
      ("/2/query/%s" % what, compat.partial(_Check, ["name"] * 4), "PUT", {
307
         "fields": ["name"] * 4,
308
         }),
309
      ])
310

    
311
    def _CheckFilter():
312
      _DoTests([
313
        # With filter
314
        ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
315
           "fields": all_fields,
316
           "filter": [qlang.OP_TRUE, "name"],
317
           }),
318
        ])
319

    
320
    if what == constants.QR_LOCK:
321
      # Locks can't be filtered
322
      try:
323
        _CheckFilter()
324
      except rapi.client.GanetiApiError, err:
325
        AssertEqual(err.code, 500)
326
      else:
327
        raise qa_error.Error("Filtering locks didn't fail")
328
    else:
329
      _CheckFilter()
330

    
331
    if what == constants.QR_NODE:
332
      # Test with filter
333
      (nodes, ) = _DoTests([("/2/query/%s" % what,
334
        compat.partial(_Check, ["name", "master"]), "PUT", {
335
        "fields": ["name", "master"],
336
        "filter": [qlang.OP_TRUE, "master"],
337
        })])
338
      qresult = objects.QueryResponse.FromDict(nodes)
339
      AssertEqual(qresult.data, [
340
        [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
341
        ])
342

    
343

    
344
def TestInstance(instance):
345
  """Testing getting instance(s) info via remote API.
346

347
  """
348
  def _VerifyInstance(data):
349
    for entry in INSTANCE_FIELDS:
350
      AssertIn(entry, data)
351

    
352
  def _VerifyInstancesList(data):
353
    for instance in data:
354
      for entry in LIST_FIELDS:
355
        AssertIn(entry, instance)
356

    
357
  def _VerifyInstancesBulk(data):
358
    for instance_data in data:
359
      _VerifyInstance(instance_data)
360

    
361
  _DoTests([
362
    ("/2/instances/%s" % instance["name"], _VerifyInstance, "GET", None),
363
    ("/2/instances", _VerifyInstancesList, "GET", None),
364
    ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
365
    ("/2/instances/%s/activate-disks" % instance["name"],
366
     _VerifyReturnsJob, "PUT", None),
367
    ("/2/instances/%s/deactivate-disks" % instance["name"],
368
     _VerifyReturnsJob, "PUT", None),
369
    ])
370

    
371
  # Test OpBackupPrepare
372
  (job_id, ) = _DoTests([
373
    ("/2/instances/%s/prepare-export?mode=%s" %
374
     (instance["name"], constants.EXPORT_MODE_REMOTE),
375
     _VerifyReturnsJob, "PUT", None),
376
    ])
377

    
378
  result = _WaitForRapiJob(job_id)[0]
379
  AssertEqual(len(result["handshake"]), 3)
380
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
381
  AssertEqual(len(result["x509_key_name"]), 3)
382
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
383

    
384

    
385
def TestNode(node):
386
  """Testing getting node(s) info via remote API.
387

388
  """
389
  def _VerifyNode(data):
390
    for entry in NODE_FIELDS:
391
      AssertIn(entry, data)
392

    
393
  def _VerifyNodesList(data):
394
    for node in data:
395
      for entry in LIST_FIELDS:
396
        AssertIn(entry, node)
397

    
398
  def _VerifyNodesBulk(data):
399
    for node_data in data:
400
      _VerifyNode(node_data)
401

    
402
  _DoTests([
403
    ("/2/nodes/%s" % node["primary"], _VerifyNode, "GET", None),
404
    ("/2/nodes", _VerifyNodesList, "GET", None),
405
    ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
406
    ])
407

    
408

    
409
def TestTags(kind, name, tags):
410
  """Tests .../tags resources.
411

412
  """
413
  if kind == constants.TAG_CLUSTER:
414
    uri = "/2/tags"
415
  elif kind == constants.TAG_NODE:
416
    uri = "/2/nodes/%s/tags" % name
417
  elif kind == constants.TAG_INSTANCE:
418
    uri = "/2/instances/%s/tags" % name
419
  elif kind == constants.TAG_NODEGROUP:
420
    uri = "/2/groups/%s/tags" % name
421
  else:
422
    raise errors.ProgrammerError("Unknown tag kind")
423

    
424
  def _VerifyTags(data):
425
    AssertEqual(sorted(tags), sorted(data))
426

    
427
  queryargs = "&".join("tag=%s" % i for i in tags)
428

    
429
  # Add tags
430
  (job_id, ) = _DoTests([
431
    ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
432
    ])
433
  _WaitForRapiJob(job_id)
434

    
435
  # Retrieve tags
436
  _DoTests([
437
    (uri, _VerifyTags, "GET", None),
438
    ])
439

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

    
446

    
447
def _WaitForRapiJob(job_id):
448
  """Waits for a job to finish.
449

450
  """
451
  def _VerifyJob(data):
452
    AssertEqual(data["id"], job_id)
453
    for field in JOB_FIELDS:
454
      AssertIn(field, data)
455

    
456
  _DoTests([
457
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
458
    ])
459

    
460
  return rapi.client_utils.PollJob(_rapi_client, job_id,
461
                                   cli.StdioJobPollReportCb())
462

    
463

    
464
def TestRapiNodeGroups():
465
  """Test several node group operations using RAPI.
466

467
  """
468
  groups = qa_config.get("groups", {})
469
  group1, group2, group3 = groups.get("inexistent-groups",
470
                                      ["group1", "group2", "group3"])[:3]
471

    
472
  # Create a group with no attributes
473
  body = {
474
    "name": group1,
475
    }
476

    
477
  (job_id, ) = _DoTests([
478
    ("/2/groups", _VerifyReturnsJob, "POST", body),
479
    ])
480

    
481
  _WaitForRapiJob(job_id)
482

    
483
  # Create a group specifying alloc_policy
484
  body = {
485
    "name": group2,
486
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
487
    }
488

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

    
493
  _WaitForRapiJob(job_id)
494

    
495
  # Modify alloc_policy
496
  body = {
497
    "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
498
    }
499

    
500
  (job_id, ) = _DoTests([
501
    ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
502
    ])
503

    
504
  _WaitForRapiJob(job_id)
505

    
506
  # Rename a group
507
  body = {
508
    "new_name": group3,
509
    }
510

    
511
  (job_id, ) = _DoTests([
512
    ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
513
    ])
514

    
515
  _WaitForRapiJob(job_id)
516

    
517
  # Delete groups
518
  for group in [group1, group3]:
519
    (job_id, ) = _DoTests([
520
      ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
521
      ])
522

    
523
    _WaitForRapiJob(job_id)
524

    
525

    
526
def TestRapiInstanceAdd(node, use_client):
527
  """Test adding a new instance via RAPI"""
528
  instance = qa_config.AcquireInstance()
529
  try:
530
    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
531
    disks = [{"size": size} for size in disk_sizes]
532
    nics = [{}]
533

    
534
    beparams = {
535
      constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
536
      constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
537
      }
538

    
539
    if use_client:
540
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
541
                                           instance["name"],
542
                                           constants.DT_PLAIN,
543
                                           disks, nics,
544
                                           os=qa_config.get("os"),
545
                                           pnode=node["primary"],
546
                                           beparams=beparams)
547
    else:
548
      body = {
549
        "__version__": 1,
550
        "mode": constants.INSTANCE_CREATE,
551
        "name": instance["name"],
552
        "os_type": qa_config.get("os"),
553
        "disk_template": constants.DT_PLAIN,
554
        "pnode": node["primary"],
555
        "beparams": beparams,
556
        "disks": disks,
557
        "nics": nics,
558
        }
559

    
560
      (job_id, ) = _DoTests([
561
        ("/2/instances", _VerifyReturnsJob, "POST", body),
562
        ])
563

    
564
    _WaitForRapiJob(job_id)
565

    
566
    return instance
567
  except:
568
    qa_config.ReleaseInstance(instance)
569
    raise
570

    
571

    
572
def TestRapiInstanceRemove(instance, use_client):
573
  """Test removing instance via RAPI"""
574
  if use_client:
575
    job_id = _rapi_client.DeleteInstance(instance["name"])
576
  else:
577
    (job_id, ) = _DoTests([
578
      ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
579
      ])
580

    
581
  _WaitForRapiJob(job_id)
582

    
583
  qa_config.ReleaseInstance(instance)
584

    
585

    
586
def TestRapiInstanceMigrate(instance):
587
  """Test migrating instance via RAPI"""
588
  # Move to secondary node
589
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
590
  # And back to previous primary
591
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
592

    
593

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

    
601

    
602
def TestRapiInstanceShutdown(instance):
603
  """Test stopping an instance via RAPI"""
604
  _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
605

    
606

    
607
def TestRapiInstanceStartup(instance):
608
  """Test starting an instance via RAPI"""
609
  _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
610

    
611

    
612
def TestRapiInstanceRenameAndBack(rename_source, rename_target):
613
  """Test renaming instance via RAPI
614

615
  This must leave the instance with the original name (in the
616
  non-failure case).
617

618
  """
619
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
620
  _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
621

    
622

    
623
def TestRapiInstanceReinstall(instance):
624
  """Test reinstalling an instance via RAPI"""
625
  _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
626

    
627

    
628
def TestRapiInstanceReplaceDisks(instance):
629
  """Test replacing instance disks via RAPI"""
630
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
631
    mode=constants.REPLACE_DISK_AUTO, disks=[]))
632
  _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
633
    mode=constants.REPLACE_DISK_SEC, disks="0"))
634

    
635

    
636
def TestRapiInstanceModify(instance):
637
  """Test modifying instance via RAPI"""
638
  def _ModifyInstance(**kwargs):
639
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
640

    
641
  _ModifyInstance(hvparams={
642
    constants.HV_KERNEL_ARGS: "single",
643
    })
644

    
645
  _ModifyInstance(beparams={
646
    constants.BE_VCPUS: 3,
647
    })
648

    
649
  _ModifyInstance(beparams={
650
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
651
    })
652

    
653
  _ModifyInstance(hvparams={
654
    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
655
    })
656

    
657

    
658
def TestRapiInstanceConsole(instance):
659
  """Test getting instance console information via RAPI"""
660
  result = _rapi_client.GetInstanceConsole(instance["name"])
661
  console = objects.InstanceConsole.FromDict(result)
662
  AssertEqual(console.Validate(), True)
663
  AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
664

    
665

    
666
def TestRapiStoppedInstanceConsole(instance):
667
  """Test getting stopped instance's console information via RAPI"""
668
  try:
669
    _rapi_client.GetInstanceConsole(instance["name"])
670
  except rapi.client.GanetiApiError, err:
671
    AssertEqual(err.code, 503)
672
  else:
673
    raise qa_error.Error("Getting console for stopped instance didn't"
674
                         " return HTTP 503")
675

    
676

    
677
def GetOperatingSystems():
678
  """Retrieves a list of all available operating systems.
679

680
  """
681
  return _rapi_client.GetOperatingSystems()
682

    
683

    
684
def TestInterClusterInstanceMove(src_instance, dest_instance,
685
                                 pnode, snode, tnode):
686
  """Test tools/move-instance"""
687
  master = qa_config.GetMasterNode()
688

    
689
  rapi_pw_file = tempfile.NamedTemporaryFile()
690
  rapi_pw_file.write(_rapi_password)
691
  rapi_pw_file.flush()
692

    
693
  # TODO: Run some instance tests before moving back
694

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

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