QA: Decorate tests with instance checks
[ganeti-local] / qa / qa_rapi.py
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_UP, INST_UP, 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, True)
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
649
650 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
651 def TestRapiInstanceReplaceDisks(instance):
652   """Test replacing instance disks via RAPI"""
653   _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
654     mode=constants.REPLACE_DISK_AUTO, disks=[]))
655   _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
656     mode=constants.REPLACE_DISK_SEC, disks="0"))
657
658
659 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
660 def TestRapiInstanceModify(instance):
661   """Test modifying instance via RAPI"""
662   def _ModifyInstance(**kwargs):
663     _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
664
665   _ModifyInstance(hvparams={
666     constants.HV_KERNEL_ARGS: "single",
667     })
668
669   _ModifyInstance(beparams={
670     constants.BE_VCPUS: 3,
671     })
672
673   _ModifyInstance(beparams={
674     constants.BE_VCPUS: constants.VALUE_DEFAULT,
675     })
676
677   _ModifyInstance(hvparams={
678     constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
679     })
680
681
682 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
683 def TestRapiInstanceConsole(instance):
684   """Test getting instance console information via RAPI"""
685   result = _rapi_client.GetInstanceConsole(instance["name"])
686   console = objects.InstanceConsole.FromDict(result)
687   AssertEqual(console.Validate(), True)
688   AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
689
690
691 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
692 def TestRapiStoppedInstanceConsole(instance):
693   """Test getting stopped instance's console information via RAPI"""
694   try:
695     _rapi_client.GetInstanceConsole(instance["name"])
696   except rapi.client.GanetiApiError, err:
697     AssertEqual(err.code, 503)
698   else:
699     raise qa_error.Error("Getting console for stopped instance didn't"
700                          " return HTTP 503")
701
702
703 def GetOperatingSystems():
704   """Retrieves a list of all available operating systems.
705
706   """
707   return _rapi_client.GetOperatingSystems()
708
709
710 def TestInterClusterInstanceMove(src_instance, dest_instance,
711                                  pnode, snode, tnode):
712   """Test tools/move-instance"""
713   master = qa_config.GetMasterNode()
714
715   rapi_pw_file = tempfile.NamedTemporaryFile()
716   rapi_pw_file.write(_rapi_password)
717   rapi_pw_file.flush()
718
719   # TODO: Run some instance tests before moving back
720
721   if snode is None:
722     # instance is not redundant, but we still need to pass a node
723     # (which will be ignored)
724     fsec = tnode
725   else:
726     fsec = snode
727   # note: pnode:snode are the *current* nodes, so we move it first to
728   # tnode:pnode, then back to pnode:snode
729   for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
730                           tnode["primary"], pnode["primary"]),
731                          (dest_instance["name"], src_instance["name"],
732                           pnode["primary"], fsec["primary"])]:
733     cmd = [
734       "../tools/move-instance",
735       "--verbose",
736       "--src-ca-file=%s" % _rapi_ca.name,
737       "--src-username=%s" % _rapi_username,
738       "--src-password-file=%s" % rapi_pw_file.name,
739       "--dest-instance-name=%s" % di,
740       "--dest-primary-node=%s" % pn,
741       "--dest-secondary-node=%s" % sn,
742       "--net=0:mac=%s" % constants.VALUE_GENERATE,
743       master["primary"],
744       master["primary"],
745       si,
746       ]
747
748     qa_utils.RunInstanceCheck(di, False)
749     AssertEqual(StartLocalCommand(cmd).wait(), 0)
750     qa_utils.RunInstanceCheck(si, False)
751     qa_utils.RunInstanceCheck(di, True)