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