QA: Use lists of nodes as argument to instance tests
[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   qa_config.ReleaseInstance(instance)
618
619
620 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
621 def TestRapiInstanceMigrate(instance):
622   """Test migrating instance via RAPI"""
623   if not IsMigrationSupported(instance):
624     print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
625                               " test")
626     return
627   # Move to secondary node
628   _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
629   qa_utils.RunInstanceCheck(instance, True)
630   # And back to previous primary
631   _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
632
633
634 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
635 def TestRapiInstanceFailover(instance):
636   """Test failing over instance via RAPI"""
637   if not IsFailoverSupported(instance):
638     print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
639                               " test")
640     return
641   # Move to secondary node
642   _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
643   qa_utils.RunInstanceCheck(instance, True)
644   # And back to previous primary
645   _WaitForRapiJob(_rapi_client.FailoverInstance(instance["name"]))
646
647
648 @InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
649 def TestRapiInstanceShutdown(instance):
650   """Test stopping an instance via RAPI"""
651   _WaitForRapiJob(_rapi_client.ShutdownInstance(instance["name"]))
652
653
654 @InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
655 def TestRapiInstanceStartup(instance):
656   """Test starting an instance via RAPI"""
657   _WaitForRapiJob(_rapi_client.StartupInstance(instance["name"]))
658
659
660 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
661 def TestRapiInstanceRenameAndBack(rename_source, rename_target):
662   """Test renaming instance via RAPI
663
664   This must leave the instance with the original name (in the
665   non-failure case).
666
667   """
668   _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
669   qa_utils.RunInstanceCheck(rename_source, False)
670   qa_utils.RunInstanceCheck(rename_target, False)
671   _WaitForRapiJob(_rapi_client.RenameInstance(rename_target, rename_source))
672   qa_utils.RunInstanceCheck(rename_target, False)
673
674
675 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
676 def TestRapiInstanceReinstall(instance):
677   """Test reinstalling an instance via RAPI"""
678   _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"]))
679   # By default, the instance is started again
680   qa_utils.RunInstanceCheck(instance, True)
681
682   # Reinstall again without starting
683   _WaitForRapiJob(_rapi_client.ReinstallInstance(instance["name"],
684                                                  no_startup=True))
685
686
687 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
688 def TestRapiInstanceReplaceDisks(instance):
689   """Test replacing instance disks via RAPI"""
690   if not IsDiskReplacingSupported(instance):
691     print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
692                               " skipping test")
693     return
694   fn = _rapi_client.ReplaceInstanceDisks
695   _WaitForRapiJob(fn(instance["name"],
696                      mode=constants.REPLACE_DISK_AUTO, disks=[]))
697   _WaitForRapiJob(fn(instance["name"],
698                      mode=constants.REPLACE_DISK_SEC, disks="0"))
699
700
701 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
702 def TestRapiInstanceModify(instance):
703   """Test modifying instance via RAPI"""
704   default_hv = qa_config.GetDefaultHypervisor()
705
706   def _ModifyInstance(**kwargs):
707     _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
708
709   _ModifyInstance(beparams={
710     constants.BE_VCPUS: 3,
711     })
712
713   _ModifyInstance(beparams={
714     constants.BE_VCPUS: constants.VALUE_DEFAULT,
715     })
716
717   if default_hv == constants.HT_XEN_PVM:
718     _ModifyInstance(hvparams={
719       constants.HV_KERNEL_ARGS: "single",
720       })
721     _ModifyInstance(hvparams={
722       constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
723       })
724   elif default_hv == constants.HT_XEN_HVM:
725     _ModifyInstance(hvparams={
726       constants.HV_BOOT_ORDER: "acn",
727       })
728     _ModifyInstance(hvparams={
729       constants.HV_BOOT_ORDER: constants.VALUE_DEFAULT,
730       })
731
732
733 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
734 def TestRapiInstanceConsole(instance):
735   """Test getting instance console information via RAPI"""
736   result = _rapi_client.GetInstanceConsole(instance["name"])
737   console = objects.InstanceConsole.FromDict(result)
738   AssertEqual(console.Validate(), True)
739   AssertEqual(console.instance, qa_utils.ResolveInstanceName(instance["name"]))
740
741
742 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
743 def TestRapiStoppedInstanceConsole(instance):
744   """Test getting stopped instance's console information via RAPI"""
745   try:
746     _rapi_client.GetInstanceConsole(instance["name"])
747   except rapi.client.GanetiApiError, err:
748     AssertEqual(err.code, 503)
749   else:
750     raise qa_error.Error("Getting console for stopped instance didn't"
751                          " return HTTP 503")
752
753
754 def GetOperatingSystems():
755   """Retrieves a list of all available operating systems.
756
757   """
758   return _rapi_client.GetOperatingSystems()
759
760
761 def TestInterClusterInstanceMove(src_instance, dest_instance,
762                                  inodes, tnode):
763   """Test tools/move-instance"""
764   master = qa_config.GetMasterNode()
765
766   rapi_pw_file = tempfile.NamedTemporaryFile()
767   rapi_pw_file.write(_rapi_password)
768   rapi_pw_file.flush()
769
770   qa_config.SetInstanceTemplate(dest_instance,
771                                 qa_config.GetInstanceTemplate(src_instance))
772
773   # TODO: Run some instance tests before moving back
774
775   if len(inodes) > 1:
776     # No disk template currently requires more than 1 secondary node. If this
777     # changes, either this test must be skipped or the script must be updated.
778     assert len(inodes) == 2
779     snode = inodes[1]
780   else:
781     # instance is not redundant, but we still need to pass a node
782     # (which will be ignored)
783     snode = tnode
784   pnode = inodes[0]
785   # note: pnode:snode are the *current* nodes, so we move it first to
786   # tnode:pnode, then back to pnode:snode
787   for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
788                           tnode["primary"], pnode["primary"]),
789                          (dest_instance["name"], src_instance["name"],
790                           pnode["primary"], snode["primary"])]:
791     cmd = [
792       "../tools/move-instance",
793       "--verbose",
794       "--src-ca-file=%s" % _rapi_ca.name,
795       "--src-username=%s" % _rapi_username,
796       "--src-password-file=%s" % rapi_pw_file.name,
797       "--dest-instance-name=%s" % di,
798       "--dest-primary-node=%s" % pn,
799       "--dest-secondary-node=%s" % sn,
800       "--net=0:mac=%s" % constants.VALUE_GENERATE,
801       master["primary"],
802       master["primary"],
803       si,
804       ]
805
806     qa_utils.RunInstanceCheck(di, False)
807     AssertEqual(StartLocalCommand(cmd).wait(), 0)
808     qa_utils.RunInstanceCheck(si, False)
809     qa_utils.RunInstanceCheck(di, True)