Final update of NEWS file for 2.6.2 and version bump
[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 import re
29 import itertools
30
31 from ganeti import utils
32 from ganeti import constants
33 from ganeti import errors
34 from ganeti import cli
35 from ganeti import rapi
36 from ganeti import objects
37 from ganeti import query
38 from ganeti import compat
39 from ganeti import qlang
40
41 import ganeti.rapi.client        # pylint: disable=W0611
42 import ganeti.rapi.client_utils
43
44 import qa_config
45 import qa_utils
46 import qa_error
47
48 from qa_utils import (AssertEqual, AssertIn, AssertMatch, StartLocalCommand)
49 from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG
50
51
52 _rapi_ca = None
53 _rapi_client = None
54 _rapi_username = None
55 _rapi_password = None
56
57
58 def Setup(username, password):
59   """Configures the RAPI client.
60
61   """
62   # pylint: disable=W0603
63   # due to global usage
64   global _rapi_ca
65   global _rapi_client
66   global _rapi_username
67   global _rapi_password
68
69   _rapi_username = username
70   _rapi_password = password
71
72   master = qa_config.GetMasterNode()
73
74   # Load RAPI certificate from master node
75   cmd = ["cat", constants.RAPI_CERT_FILE]
76
77   # Write to temporary file
78   _rapi_ca = tempfile.NamedTemporaryFile()
79   _rapi_ca.write(qa_utils.GetCommandOutput(master["primary"],
80                                            utils.ShellQuoteArgs(cmd)))
81   _rapi_ca.flush()
82
83   port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
84   cfg_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
85                                            proxy="")
86
87   _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
88                                               username=username,
89                                               password=password,
90                                               curl_config_fn=cfg_curl)
91
92   print "RAPI protocol version: %s" % _rapi_client.GetVersion()
93
94
95 INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
96                    "admin_state",
97                    "disk_template", "disk.sizes",
98                    "nic.ips", "nic.macs", "nic.modes", "nic.links",
99                    "beparams", "hvparams",
100                    "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
101
102 NODE_FIELDS = ("name", "dtotal", "dfree",
103                "mtotal", "mnode", "mfree",
104                "pinst_cnt", "sinst_cnt", "tags")
105
106 GROUP_FIELDS = frozenset([
107   "name", "uuid",
108   "alloc_policy",
109   "node_cnt", "node_list",
110   ])
111
112 JOB_FIELDS = frozenset([
113   "id", "ops", "status", "summary",
114   "opstatus", "opresult", "oplog",
115   "received_ts", "start_ts", "end_ts",
116   ])
117
118 LIST_FIELDS = ("id", "uri")
119
120
121 def Enabled():
122   """Return whether remote API tests should be run.
123
124   """
125   return qa_config.TestEnabled("rapi")
126
127
128 def _DoTests(uris):
129   # pylint: disable=W0212
130   # due to _SendRequest usage
131   results = []
132
133   for uri, verify, method, body in uris:
134     assert uri.startswith("/")
135
136     print "%s %s" % (method, uri)
137     data = _rapi_client._SendRequest(method, uri, None, body)
138
139     if verify is not None:
140       if callable(verify):
141         verify(data)
142       else:
143         AssertEqual(data, verify)
144
145     results.append(data)
146
147   return results
148
149
150 def _VerifyReturnsJob(data):
151   AssertMatch(data, r"^\d+$")
152
153
154 def TestVersion():
155   """Testing remote API version.
156
157   """
158   _DoTests([
159     ("/version", constants.RAPI_VERSION, "GET", None),
160     ])
161
162
163 def TestEmptyCluster():
164   """Testing remote API on an empty cluster.
165
166   """
167   master = qa_config.GetMasterNode()
168   master_full = qa_utils.ResolveNodeName(master)
169
170   def _VerifyInfo(data):
171     AssertIn("name", data)
172     AssertIn("master", data)
173     AssertEqual(data["master"], master_full)
174
175   def _VerifyNodes(data):
176     master_entry = {
177       "id": master_full,
178       "uri": "/2/nodes/%s" % master_full,
179       }
180     AssertIn(master_entry, data)
181
182   def _VerifyNodesBulk(data):
183     for node in data:
184       for entry in NODE_FIELDS:
185         AssertIn(entry, node)
186
187   def _VerifyGroups(data):
188     default_group = {
189       "name": constants.INITIAL_NODE_GROUP_NAME,
190       "uri": "/2/groups/" + constants.INITIAL_NODE_GROUP_NAME,
191       }
192     AssertIn(default_group, data)
193
194   def _VerifyGroupsBulk(data):
195     for group in data:
196       for field in GROUP_FIELDS:
197         AssertIn(field, group)
198
199   _DoTests([
200     ("/", None, "GET", None),
201     ("/2/info", _VerifyInfo, "GET", None),
202     ("/2/tags", None, "GET", None),
203     ("/2/nodes", _VerifyNodes, "GET", None),
204     ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
205     ("/2/groups", _VerifyGroups, "GET", None),
206     ("/2/groups?bulk=1", _VerifyGroupsBulk, "GET", None),
207     ("/2/instances", [], "GET", None),
208     ("/2/instances?bulk=1", [], "GET", None),
209     ("/2/os", None, "GET", None),
210     ])
211
212   # Test HTTP Not Found
213   for method in ["GET", "PUT", "POST", "DELETE"]:
214     try:
215       _DoTests([("/99/resource/not/here/99", None, method, None)])
216     except rapi.client.GanetiApiError, err:
217       AssertEqual(err.code, 404)
218     else:
219       raise qa_error.Error("Non-existent resource didn't return HTTP 404")
220
221   # Test HTTP Not Implemented
222   for method in ["PUT", "POST", "DELETE"]:
223     try:
224       _DoTests([("/version", None, method, None)])
225     except rapi.client.GanetiApiError, err:
226       AssertEqual(err.code, 501)
227     else:
228       raise qa_error.Error("Non-implemented method didn't fail")
229
230
231 def TestRapiQuery():
232   """Testing resource queries via remote API.
233
234   """
235   master_name = qa_utils.ResolveNodeName(qa_config.GetMasterNode())
236   rnd = random.Random(7818)
237
238   for what in constants.QR_VIA_RAPI:
239     if what == constants.QR_JOB:
240       namefield = "id"
241     elif what == constants.QR_EXPORT:
242       namefield = "export"
243     else:
244       namefield = "name"
245
246     all_fields = query.ALL_FIELDS[what].keys()
247     rnd.shuffle(all_fields)
248
249     # No fields, should return everything
250     result = _rapi_client.QueryFields(what)
251     qresult = objects.QueryFieldsResponse.FromDict(result)
252     AssertEqual(len(qresult.fields), len(all_fields))
253
254     # One field
255     result = _rapi_client.QueryFields(what, fields=[namefield])
256     qresult = objects.QueryFieldsResponse.FromDict(result)
257     AssertEqual(len(qresult.fields), 1)
258
259     # Specify all fields, order must be correct
260     result = _rapi_client.QueryFields(what, fields=all_fields)
261     qresult = objects.QueryFieldsResponse.FromDict(result)
262     AssertEqual(len(qresult.fields), len(all_fields))
263     AssertEqual([fdef.name for fdef in qresult.fields], all_fields)
264
265     # Unknown field
266     result = _rapi_client.QueryFields(what, fields=["_unknown!"])
267     qresult = objects.QueryFieldsResponse.FromDict(result)
268     AssertEqual(len(qresult.fields), 1)
269     AssertEqual(qresult.fields[0].name, "_unknown!")
270     AssertEqual(qresult.fields[0].kind, constants.QFT_UNKNOWN)
271
272     # Try once more, this time without the client
273     _DoTests([
274       ("/2/query/%s/fields" % what, None, "GET", None),
275       ("/2/query/%s/fields?fields=name,name,%s" % (what, all_fields[0]),
276        None, "GET", None),
277       ])
278
279     # Try missing query argument
280     try:
281       _DoTests([
282         ("/2/query/%s" % what, None, "GET", None),
283         ])
284     except rapi.client.GanetiApiError, err:
285       AssertEqual(err.code, 400)
286     else:
287       raise qa_error.Error("Request missing 'fields' parameter didn't fail")
288
289     def _Check(exp_fields, data):
290       qresult = objects.QueryResponse.FromDict(data)
291       AssertEqual([fdef.name for fdef in qresult.fields], exp_fields)
292       if not isinstance(qresult.data, list):
293         raise qa_error.Error("Query did not return a list")
294
295     _DoTests([
296       # Specify fields in query
297       ("/2/query/%s?fields=%s" % (what, ",".join(all_fields)),
298        compat.partial(_Check, all_fields), "GET", None),
299
300       ("/2/query/%s?fields=%s" % (what, namefield),
301        compat.partial(_Check, [namefield]), "GET", None),
302
303       # Note the spaces
304       ("/2/query/%s?fields=%s,%%20%s%%09,%s%%20" %
305        (what, namefield, namefield, namefield),
306        compat.partial(_Check, [namefield] * 3), "GET", None),
307
308       # PUT with fields in query
309       ("/2/query/%s?fields=%s" % (what, namefield),
310        compat.partial(_Check, [namefield]), "PUT", {}),
311
312       # Fields in body
313       ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
314          "fields": all_fields,
315          }),
316
317       ("/2/query/%s" % what, compat.partial(_Check, [namefield] * 4), "PUT", {
318          "fields": [namefield] * 4,
319          }),
320       ])
321
322     def _CheckFilter():
323       _DoTests([
324         # With filter
325         ("/2/query/%s" % what, compat.partial(_Check, all_fields), "PUT", {
326            "fields": all_fields,
327            "filter": [qlang.OP_TRUE, namefield],
328            }),
329         ])
330
331     if what == constants.QR_LOCK:
332       # Locks can't be filtered
333       try:
334         _CheckFilter()
335       except rapi.client.GanetiApiError, err:
336         AssertEqual(err.code, 500)
337       else:
338         raise qa_error.Error("Filtering locks didn't fail")
339     else:
340       _CheckFilter()
341
342     if what == constants.QR_NODE:
343       # Test with filter
344       (nodes, ) = _DoTests([("/2/query/%s" % what,
345         compat.partial(_Check, ["name", "master"]), "PUT", {
346         "fields": ["name", "master"],
347         "filter": [qlang.OP_TRUE, "master"],
348         })])
349       qresult = objects.QueryResponse.FromDict(nodes)
350       AssertEqual(qresult.data, [
351         [[constants.RS_NORMAL, master_name], [constants.RS_NORMAL, True]],
352         ])
353
354
355 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
356 def TestInstance(instance):
357   """Testing getting instance(s) info via remote API.
358
359   """
360   def _VerifyInstance(data):
361     for entry in INSTANCE_FIELDS:
362       AssertIn(entry, data)
363
364   def _VerifyInstancesList(data):
365     for instance in data:
366       for entry in LIST_FIELDS:
367         AssertIn(entry, instance)
368
369   def _VerifyInstancesBulk(data):
370     for instance_data in data:
371       _VerifyInstance(instance_data)
372
373   _DoTests([
374     ("/2/instances/%s" % instance["name"], _VerifyInstance, "GET", None),
375     ("/2/instances", _VerifyInstancesList, "GET", None),
376     ("/2/instances?bulk=1", _VerifyInstancesBulk, "GET", None),
377     ("/2/instances/%s/activate-disks" % instance["name"],
378      _VerifyReturnsJob, "PUT", None),
379     ("/2/instances/%s/deactivate-disks" % instance["name"],
380      _VerifyReturnsJob, "PUT", None),
381     ])
382
383   # Test OpBackupPrepare
384   (job_id, ) = _DoTests([
385     ("/2/instances/%s/prepare-export?mode=%s" %
386      (instance["name"], constants.EXPORT_MODE_REMOTE),
387      _VerifyReturnsJob, "PUT", None),
388     ])
389
390   result = _WaitForRapiJob(job_id)[0]
391   AssertEqual(len(result["handshake"]), 3)
392   AssertEqual(result["handshake"][0], constants.RIE_VERSION)
393   AssertEqual(len(result["x509_key_name"]), 3)
394   AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
395
396
397 def TestNode(node):
398   """Testing getting node(s) info via remote API.
399
400   """
401   def _VerifyNode(data):
402     for entry in NODE_FIELDS:
403       AssertIn(entry, data)
404
405   def _VerifyNodesList(data):
406     for node in data:
407       for entry in LIST_FIELDS:
408         AssertIn(entry, node)
409
410   def _VerifyNodesBulk(data):
411     for node_data in data:
412       _VerifyNode(node_data)
413
414   _DoTests([
415     ("/2/nodes/%s" % node["primary"], _VerifyNode, "GET", None),
416     ("/2/nodes", _VerifyNodesList, "GET", None),
417     ("/2/nodes?bulk=1", _VerifyNodesBulk, "GET", None),
418     ])
419
420
421 def _FilterTags(seq):
422   """Removes unwanted tags from a sequence.
423
424   """
425   ignore_re = qa_config.get("ignore-tags-re", None)
426
427   if ignore_re:
428     return itertools.ifilterfalse(re.compile(ignore_re).match, seq)
429   else:
430     return seq
431
432
433 def TestTags(kind, name, tags):
434   """Tests .../tags resources.
435
436   """
437   if kind == constants.TAG_CLUSTER:
438     uri = "/2/tags"
439   elif kind == constants.TAG_NODE:
440     uri = "/2/nodes/%s/tags" % name
441   elif kind == constants.TAG_INSTANCE:
442     uri = "/2/instances/%s/tags" % name
443   elif kind == constants.TAG_NODEGROUP:
444     uri = "/2/groups/%s/tags" % name
445   else:
446     raise errors.ProgrammerError("Unknown tag kind")
447
448   def _VerifyTags(data):
449     AssertEqual(sorted(tags), sorted(_FilterTags(data)))
450
451   queryargs = "&".join("tag=%s" % i for i in tags)
452
453   # Add tags
454   (job_id, ) = _DoTests([
455     ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "PUT", None),
456     ])
457   _WaitForRapiJob(job_id)
458
459   # Retrieve tags
460   _DoTests([
461     (uri, _VerifyTags, "GET", None),
462     ])
463
464   # Remove tags
465   (job_id, ) = _DoTests([
466     ("%s?%s" % (uri, queryargs), _VerifyReturnsJob, "DELETE", None),
467     ])
468   _WaitForRapiJob(job_id)
469
470
471 def _WaitForRapiJob(job_id):
472   """Waits for a job to finish.
473
474   """
475   def _VerifyJob(data):
476     AssertEqual(data["id"], job_id)
477     for field in JOB_FIELDS:
478       AssertIn(field, data)
479
480   _DoTests([
481     ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
482     ])
483
484   return rapi.client_utils.PollJob(_rapi_client, job_id,
485                                    cli.StdioJobPollReportCb())
486
487
488 def TestRapiNodeGroups():
489   """Test several node group operations using RAPI.
490
491   """
492   groups = qa_config.get("groups", {})
493   group1, group2, group3 = groups.get("inexistent-groups",
494                                       ["group1", "group2", "group3"])[:3]
495
496   # Create a group with no attributes
497   body = {
498     "name": group1,
499     }
500
501   (job_id, ) = _DoTests([
502     ("/2/groups", _VerifyReturnsJob, "POST", body),
503     ])
504
505   _WaitForRapiJob(job_id)
506
507   # Create a group specifying alloc_policy
508   body = {
509     "name": group2,
510     "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
511     }
512
513   (job_id, ) = _DoTests([
514     ("/2/groups", _VerifyReturnsJob, "POST", body),
515     ])
516
517   _WaitForRapiJob(job_id)
518
519   # Modify alloc_policy
520   body = {
521     "alloc_policy": constants.ALLOC_POLICY_UNALLOCABLE,
522     }
523
524   (job_id, ) = _DoTests([
525     ("/2/groups/%s/modify" % group1, _VerifyReturnsJob, "PUT", body),
526     ])
527
528   _WaitForRapiJob(job_id)
529
530   # Rename a group
531   body = {
532     "new_name": group3,
533     }
534
535   (job_id, ) = _DoTests([
536     ("/2/groups/%s/rename" % group2, _VerifyReturnsJob, "PUT", body),
537     ])
538
539   _WaitForRapiJob(job_id)
540
541   # Delete groups
542   for group in [group1, group3]:
543     (job_id, ) = _DoTests([
544       ("/2/groups/%s" % group, _VerifyReturnsJob, "DELETE", None),
545       ])
546
547     _WaitForRapiJob(job_id)
548
549
550 def TestRapiInstanceAdd(node, use_client):
551   """Test adding a new instance via RAPI"""
552   instance = qa_config.AcquireInstance()
553   try:
554     disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
555     disks = [{"size": size} for size in disk_sizes]
556     nic0_mac = qa_config.GetInstanceNicMac(instance,
557                                            default=constants.VALUE_GENERATE)
558     nics = [{
559       constants.INIC_MAC: nic0_mac,
560       }]
561
562     beparams = {
563       constants.BE_MAXMEM: utils.ParseUnit(qa_config.get(constants.BE_MAXMEM)),
564       constants.BE_MINMEM: utils.ParseUnit(qa_config.get(constants.BE_MINMEM)),
565       }
566
567     if use_client:
568       job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
569                                            instance["name"],
570                                            constants.DT_PLAIN,
571                                            disks, nics,
572                                            os=qa_config.get("os"),
573                                            pnode=node["primary"],
574                                            beparams=beparams)
575     else:
576       body = {
577         "__version__": 1,
578         "mode": constants.INSTANCE_CREATE,
579         "name": instance["name"],
580         "os_type": qa_config.get("os"),
581         "disk_template": constants.DT_PLAIN,
582         "pnode": node["primary"],
583         "beparams": beparams,
584         "disks": disks,
585         "nics": nics,
586         }
587
588       (job_id, ) = _DoTests([
589         ("/2/instances", _VerifyReturnsJob, "POST", body),
590         ])
591
592     _WaitForRapiJob(job_id)
593
594     return instance
595   except:
596     qa_config.ReleaseInstance(instance)
597     raise
598
599
600 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
601 def TestRapiInstanceRemove(instance, use_client):
602   """Test removing instance via RAPI"""
603   if use_client:
604     job_id = _rapi_client.DeleteInstance(instance["name"])
605   else:
606     (job_id, ) = _DoTests([
607       ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
608       ])
609
610   _WaitForRapiJob(job_id)
611
612   qa_config.ReleaseInstance(instance)
613
614
615 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
616 def TestRapiInstanceMigrate(instance):
617   """Test migrating instance via RAPI"""
618   # Move to secondary node
619   _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
620   qa_utils.RunInstanceCheck(instance, True)
621   # And back to previous primary
622   _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
623
624
625 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
626 def TestRapiInstanceFailover(instance):
627   """Test failing over instance via RAPI"""
628   # Move to secondary node
629   _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
630   qa_utils.RunInstanceCheck(instance, True)
631   # And back to previous primary
632   _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
633
634
635 @InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
636 def TestRapiInstanceShutdown(instance):
637   """Test stopping an instance via RAPI"""
638   _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
639
640
641 @InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
642 def TestRapiInstanceStartup(instance):
643   """Test starting an instance via RAPI"""
644   _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
645
646
647 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
648 def TestRapiInstanceRenameAndBack(rename_source, rename_target):
649   """Test renaming instance via RAPI
650
651   This must leave the instance with the original name (in the
652   non-failure case).
653
654   """
655   _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
656   qa_utils.RunInstanceCheck(rename_source, False)
657   qa_utils.RunInstanceCheck(rename_target, False)
658   _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
659   qa_utils.RunInstanceCheck(rename_target, False)
660
661
662 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
663 def TestRapiInstanceReinstall(instance):
664   """Test reinstalling an instance via RAPI"""
665   _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
666   # By default, the instance is started again
667   qa_utils.RunInstanceCheck(instance, True)
668
669   # Reinstall again without starting
670   _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"],
671                                                  no_startup=True))
672
673
674 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
675 def TestRapiInstanceReplaceDisks(instance):
676   """Test replacing instance disks via RAPI"""
677   _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
678     mode=constants.REPLACE_DISK_AUTO, disks=[]))
679   _WaitForRapiJob(_rapi_client.ReplaceInstanceDisks(instance["name"],
680     mode=constants.REPLACE_DISK_SEC, disks="0"))
681
682
683 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
684 def TestRapiInstanceModify(instance):
685   """Test modifying instance via RAPI"""
686   default_hv = qa_config.GetDefaultHypervisor()
687
688   def _ModifyInstance(**kwargs):
689     _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
690
691   _ModifyInstance(beparams={
692     constants.BE_VCPUS: 3,
693     })
694
695   _ModifyInstance(beparams={
696     constants.BE_VCPUS: constants.VALUE_DEFAULT,
697     })
698
699   if default_hv == constants.HT_XEN_PVM:
700     _ModifyInstance(hvparams={
701       constants.HV_KERNEL_ARGS: "single",
702       })
703     _ModifyInstance(hvparams={
704       constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
705       })
706   elif default_hv == constants.HT_XEN_HVM:
707     _ModifyInstance(hvparams={
708       constants.HV_BOOT_ORDER: "acn",
709       })
710     _ModifyInstance(hvparams={
711       constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
712       })
713
714
715 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
716 def TestRapiInstanceConsole(instance):
717   """Test getting instance console information via RAPI"""
718   result = _rapi_client.GetInstanceConsole(instance["name"])
719   console = objects.InstanceConsole.FromDict(result)
720   AssertEqual(console.Validate(), True)
721   AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
722
723
724 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
725 def TestRapiStoppedInstanceConsole(instance):
726   """Test getting stopped instance's console information via RAPI"""
727   try:
728     _rapi_client.GetInstanceConsole(instance["name"])
729   except rapi.client.GanetiApiError, err:
730     AssertEqual(err.code, 503)
731   else:
732     raise qa_error.Error("Getting console for stopped instance didn't"
733                          " return HTTP 503")
734
735
736 def GetOperatingSystems():
737   """Retrieves a list of all available operating systems.
738
739   """
740   return _rapi_client.GetOperatingSystems()
741
742
743 def TestInterClusterInstanceMove(src_instance, dest_instance,
744                                  pnode, snode, tnode):
745   """Test tools/move-instance"""
746   master = qa_config.GetMasterNode()
747
748   rapi_pw_file = tempfile.NamedTemporaryFile()
749   rapi_pw_file.write(_rapi_password)
750   rapi_pw_file.flush()
751
752   # TODO: Run some instance tests before moving back
753
754   if snode is None:
755     # instance is not redundant, but we still need to pass a node
756     # (which will be ignored)
757     fsec = tnode
758   else:
759     fsec = snode
760   # note: pnode:snode are the *current* nodes, so we move it first to
761   # tnode:pnode, then back to pnode:snode
762   for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
763                           tnode["primary"], pnode["primary"]),
764                          (dest_instance["name"], src_instance["name"],
765                           pnode["primary"], fsec["primary"])]:
766     cmd = [
767       "../tools/move-instance",
768       "--verbose",
769       "--src-ca-file=%s" % _rapi_ca.name,
770       "--src-username=%s" % _rapi_username,
771       "--src-password-file=%s" % rapi_pw_file.name,
772       "--dest-instance-name=%s" % di,
773       "--dest-primary-node=%s" % pn,
774       "--dest-secondary-node=%s" % sn,
775       "--net=0:mac=%s" % constants.VALUE_GENERATE,
776       master["primary"],
777       master["primary"],
778       si,
779       ]
780
781     qa_utils.RunInstanceCheck(di, False)
782     AssertEqual(StartLocalCommand(cmd).wait(), 0)
783     qa_utils.RunInstanceCheck(si, False)
784     qa_utils.RunInstanceCheck(di, True)