3 # Copyright (C) 2007, 2008, 2009, 2010 Google Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 """Remote API QA tests.
27 from ganeti import utils
28 from ganeti import constants
29 from ganeti import errors
30 from ganeti import serializer
31 from ganeti import cli
32 from ganeti import rapi
34 import ganeti.rapi.client
35 import ganeti.rapi.client_utils
41 from qa_utils import (AssertEqual, AssertNotEqual, AssertIn, AssertMatch,
51 def Setup(username, password):
52 """Configures the RAPI client.
60 _rapi_username = username
61 _rapi_password = password
63 master = qa_config.GetMasterNode()
65 # Load RAPI certificate from master node
66 cmd = ["cat", constants.RAPI_CERT_FILE]
68 # Write to temporary file
69 _rapi_ca = tempfile.NamedTemporaryFile()
70 _rapi_ca.write(qa_utils.GetCommandOutput(master["primary"],
71 utils.ShellQuoteArgs(cmd)))
74 port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
75 cfg_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
78 _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
81 curl_config_fn=cfg_curl)
83 print "RAPI protocol version: %s" % _rapi_client.GetVersion()
86 INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
88 "disk_template", "disk.sizes",
89 "nic.ips", "nic.macs", "nic.modes", "nic.links",
90 "beparams", "hvparams",
91 "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
93 NODE_FIELDS = ("name", "dtotal", "dfree",
94 "mtotal", "mnode", "mfree",
95 "pinst_cnt", "sinst_cnt", "tags")
97 GROUP_FIELDS = frozenset([
99 "node_cnt", "node_list",
102 JOB_FIELDS = frozenset([
103 "id", "ops", "status", "summary",
104 "opstatus", "opresult", "oplog",
105 "received_ts", "start_ts", "end_ts",
108 LIST_FIELDS = ("id", "uri")
112 """Return whether remote API tests should be run.
115 return qa_config.TestEnabled('rapi')
121 for uri, verify, method, body in uris:
122 assert uri.startswith("/")
124 print "%s %s" % (method, uri)
125 data = _rapi_client._SendRequest(method, uri, None, body)
127 if verify is not None:
131 AssertEqual(data, verify)
138 def _VerifyReturnsJob(data):
139 AssertMatch(data, r'^\d+$')
143 """Testing remote API version.
147 ("/version", constants.RAPI_VERSION, 'GET', None),
151 def TestEmptyCluster():
152 """Testing remote API on an empty cluster.
155 master = qa_config.GetMasterNode()
156 master_full = qa_utils.ResolveNodeName(master)
158 def _VerifyInfo(data):
159 AssertIn("name", data)
160 AssertIn("master", data)
161 AssertEqual(data["master"], master_full)
163 def _VerifyNodes(data):
166 "uri": "/2/nodes/%s" % master_full,
168 AssertIn(master_entry, data)
170 def _VerifyNodesBulk(data):
172 for entry in NODE_FIELDS:
173 AssertIn(entry, node)
175 def _VerifyGroups(data):
177 "name": constants.INITIAL_NODE_GROUP_NAME,
178 "uri": "/2/groups/" + constants.INITIAL_NODE_GROUP_NAME,
180 AssertIn(default_group, data)
182 def _VerifyGroupsBulk(data):
184 for field in GROUP_FIELDS:
185 AssertIn(field, group)
188 ("/", None, 'GET', None),
189 ("/2/info", _VerifyInfo, 'GET', None),
190 ("/2/tags", None, 'GET', None),
191 ("/2/nodes", _VerifyNodes, 'GET', None),
192 ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
193 ("/2/groups", _VerifyGroups, 'GET', None),
194 ("/2/groups?bulk=1", _VerifyGroupsBulk, 'GET', None),
195 ("/2/instances", [], 'GET', None),
196 ("/2/instances?bulk=1", [], 'GET', None),
197 ("/2/os", None, 'GET', None),
200 # Test HTTP Not Found
201 for method in ["GET", "PUT", "POST", "DELETE"]:
203 _DoTests([("/99/resource/not/here/99", None, method, None)])
204 except rapi.client.GanetiApiError, err:
205 AssertEqual(err.code, 404)
207 raise qa_error.Error("Non-existent resource didn't return HTTP 404")
209 # Test HTTP Not Implemented
210 for method in ["PUT", "POST", "DELETE"]:
212 _DoTests([("/version", None, method, None)])
213 except rapi.client.GanetiApiError, err:
214 AssertEqual(err.code, 501)
216 raise qa_error.Error("Non-implemented method didn't fail")
219 def TestInstance(instance):
220 """Testing getting instance(s) info via remote API.
223 def _VerifyInstance(data):
224 for entry in INSTANCE_FIELDS:
225 AssertIn(entry, data)
227 def _VerifyInstancesList(data):
228 for instance in data:
229 for entry in LIST_FIELDS:
230 AssertIn(entry, instance)
232 def _VerifyInstancesBulk(data):
233 for instance_data in data:
234 _VerifyInstance(instance_data)
237 ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET', None),
238 ("/2/instances", _VerifyInstancesList, 'GET', None),
239 ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET', None),
240 ("/2/instances/%s/activate-disks" % instance["name"],
241 _VerifyReturnsJob, 'PUT', None),
242 ("/2/instances/%s/deactivate-disks" % instance["name"],
243 _VerifyReturnsJob, 'PUT', None),
246 # Test OpPrepareExport
247 (job_id, ) = _DoTests([
248 ("/2/instances/%s/prepare-export?mode=%s" %
249 (instance["name"], constants.EXPORT_MODE_REMOTE),
250 _VerifyReturnsJob, "PUT", None),
253 result = _WaitForRapiJob(job_id)[0]
254 AssertEqual(len(result["handshake"]), 3)
255 AssertEqual(result["handshake"][0], constants.RIE_VERSION)
256 AssertEqual(len(result["x509_key_name"]), 3)
257 AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
261 """Testing getting node(s) info via remote API.
264 def _VerifyNode(data):
265 for entry in NODE_FIELDS:
266 AssertIn(entry, data)
268 def _VerifyNodesList(data):
270 for entry in LIST_FIELDS:
271 AssertIn(entry, node)
273 def _VerifyNodesBulk(data):
274 for node_data in data:
275 _VerifyNode(node_data)
278 ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET', None),
279 ("/2/nodes", _VerifyNodesList, 'GET', None),
280 ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
284 def TestTags(kind, name, tags):
285 """Tests .../tags resources.
288 if kind == constants.TAG_CLUSTER:
290 elif kind == constants.TAG_NODE:
291 uri = "/2/nodes/%s/tags" % name
292 elif kind == constants.TAG_INSTANCE:
293 uri = "/2/instances/%s/tags" % name
295 raise errors.ProgrammerError("Unknown tag kind")
297 def _VerifyTags(data):
298 AssertEqual(sorted(tags), sorted(data))
300 query = "&".join("tag=%s" % i for i in tags)
303 (job_id, ) = _DoTests([
304 ("%s?%s" % (uri, query), _VerifyReturnsJob, "PUT", None),
306 _WaitForRapiJob(job_id)
310 (uri, _VerifyTags, 'GET', None),
314 (job_id, ) = _DoTests([
315 ("%s?%s" % (uri, query), _VerifyReturnsJob, "DELETE", None),
317 _WaitForRapiJob(job_id)
320 def _WaitForRapiJob(job_id):
321 """Waits for a job to finish.
324 master = qa_config.GetMasterNode()
326 def _VerifyJob(data):
327 AssertEqual(data["id"], job_id)
328 for field in JOB_FIELDS:
329 AssertIn(field, data)
332 ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
335 return rapi.client_utils.PollJob(_rapi_client, job_id,
336 cli.StdioJobPollReportCb())
339 def TestRapiInstanceAdd(node, use_client):
340 """Test adding a new instance via RAPI"""
341 instance = qa_config.AcquireInstance()
343 memory = utils.ParseUnit(qa_config.get("mem"))
344 disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
347 disks = [{"size": size} for size in disk_sizes]
351 constants.BE_MEMORY: memory,
354 job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
358 os=qa_config.get("os"),
359 pnode=node["primary"],
363 "name": instance["name"],
364 "os": qa_config.get("os"),
365 "disk_template": constants.DT_PLAIN,
366 "pnode": node["primary"],
371 (job_id, ) = _DoTests([
372 ("/2/instances", _VerifyReturnsJob, "POST", body),
375 _WaitForRapiJob(job_id)
379 qa_config.ReleaseInstance(instance)
383 def TestRapiInstanceRemove(instance, use_client):
384 """Test removing instance via RAPI"""
386 job_id = _rapi_client.DeleteInstance(instance["name"])
388 (job_id, ) = _DoTests([
389 ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
392 _WaitForRapiJob(job_id)
394 qa_config.ReleaseInstance(instance)
397 def TestRapiInstanceMigrate(instance):
398 """Test migrating instance via RAPI"""
399 # Move to secondary node
400 _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
401 # And back to previous primary
402 _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
405 def TestRapiInstanceRename(rename_source, rename_target):
406 """Test renaming instance via RAPI"""
407 _WaitForRapiJob(_rapi_client.RenameInstance(rename_source, rename_target))
410 def TestRapiInstanceModify(instance):
411 """Test modifying instance via RAPI"""
412 def _ModifyInstance(**kwargs):
413 _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
415 _ModifyInstance(hvparams={
416 constants.HV_KERNEL_ARGS: "single",
419 _ModifyInstance(beparams={
420 constants.BE_VCPUS: 3,
423 _ModifyInstance(beparams={
424 constants.BE_VCPUS: constants.VALUE_DEFAULT,
427 _ModifyInstance(hvparams={
428 constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
432 def TestInterClusterInstanceMove(src_instance, dest_instance,
433 pnode, snode, tnode):
434 """Test tools/move-instance"""
435 master = qa_config.GetMasterNode()
437 rapi_pw_file = tempfile.NamedTemporaryFile()
438 rapi_pw_file.write(_rapi_password)
441 # TODO: Run some instance tests before moving back
444 # instance is not redundant, but we still need to pass a node
445 # (which will be ignored)
449 # note: pnode:snode are the *current* nodes, so we move it first to
450 # tnode:pnode, then back to pnode:snode
451 for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
452 tnode["primary"], pnode["primary"]),
453 (dest_instance["name"], src_instance["name"],
454 pnode["primary"], fsec["primary"])]:
456 "../tools/move-instance",
458 "--src-ca-file=%s" % _rapi_ca.name,
459 "--src-username=%s" % _rapi_username,
460 "--src-password-file=%s" % rapi_pw_file.name,
461 "--dest-instance-name=%s" % di,
462 "--dest-primary-node=%s" % pn,
463 "--dest-secondary-node=%s" % sn,
464 "--net=0:mac=%s" % constants.VALUE_GENERATE,
470 AssertEqual(StartLocalCommand(cmd).wait(), 0)