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