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