3 # Copyright (C) 2007, 2008 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 JOB_FIELDS = frozenset([
98 "id", "ops", "status", "summary",
99 "opstatus", "opresult", "oplog",
100 "received_ts", "start_ts", "end_ts",
103 LIST_FIELDS = ("id", "uri")
107 """Return whether remote API tests should be run.
110 return qa_config.TestEnabled('rapi')
116 for uri, verify, method, body in uris:
117 assert uri.startswith("/")
119 data = _rapi_client._SendRequest(method, uri, None, body)
121 if verify is not None:
125 AssertEqual(data, verify)
132 def _VerifyReturnsJob(data):
133 AssertMatch(data, r'^\d+$')
137 """Testing remote API version.
141 ("/version", constants.RAPI_VERSION, 'GET', None),
145 def TestEmptyCluster():
146 """Testing remote API on an empty cluster.
149 master = qa_config.GetMasterNode()
150 master_full = qa_utils.ResolveNodeName(master)
152 def _VerifyInfo(data):
153 AssertIn("name", data)
154 AssertIn("master", data)
155 AssertEqual(data["master"], master_full)
157 def _VerifyNodes(data):
160 "uri": "/2/nodes/%s" % master_full,
162 AssertIn(master_entry, data)
164 def _VerifyNodesBulk(data):
166 for entry in NODE_FIELDS:
167 AssertIn(entry, node)
170 ("/", None, 'GET', None),
171 ("/2/info", _VerifyInfo, 'GET', None),
172 ("/2/tags", None, 'GET', None),
173 ("/2/nodes", _VerifyNodes, 'GET', None),
174 ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
175 ("/2/instances", [], 'GET', None),
176 ("/2/instances?bulk=1", [], 'GET', None),
177 ("/2/os", None, 'GET', None),
181 def TestInstance(instance):
182 """Testing getting instance(s) info via remote API.
185 def _VerifyInstance(data):
186 for entry in INSTANCE_FIELDS:
187 AssertIn(entry, data)
189 def _VerifyInstancesList(data):
190 for instance in data:
191 for entry in LIST_FIELDS:
192 AssertIn(entry, instance)
194 def _VerifyInstancesBulk(data):
195 for instance_data in data:
196 _VerifyInstance(instance_data)
199 ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET', None),
200 ("/2/instances", _VerifyInstancesList, 'GET', None),
201 ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET', None),
202 ("/2/instances/%s/activate-disks" % instance["name"],
203 _VerifyReturnsJob, 'PUT', None),
204 ("/2/instances/%s/deactivate-disks" % instance["name"],
205 _VerifyReturnsJob, 'PUT', None),
208 # Test OpPrepareExport
209 (job_id, ) = _DoTests([
210 ("/2/instances/%s/prepare-export?mode=%s" %
211 (instance["name"], constants.EXPORT_MODE_REMOTE),
212 _VerifyReturnsJob, "PUT", None),
215 result = _WaitForRapiJob(job_id)[0]
216 AssertEqual(len(result["handshake"]), 3)
217 AssertEqual(result["handshake"][0], constants.RIE_VERSION)
218 AssertEqual(len(result["x509_key_name"]), 3)
219 AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
223 """Testing getting node(s) info via remote API.
226 def _VerifyNode(data):
227 for entry in NODE_FIELDS:
228 AssertIn(entry, data)
230 def _VerifyNodesList(data):
232 for entry in LIST_FIELDS:
233 AssertIn(entry, node)
235 def _VerifyNodesBulk(data):
236 for node_data in data:
237 _VerifyNode(node_data)
240 ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET', None),
241 ("/2/nodes", _VerifyNodesList, 'GET', None),
242 ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
246 def TestTags(kind, name, tags):
247 """Tests .../tags resources.
250 if kind == constants.TAG_CLUSTER:
252 elif kind == constants.TAG_NODE:
253 uri = "/2/nodes/%s/tags" % name
254 elif kind == constants.TAG_INSTANCE:
255 uri = "/2/instances/%s/tags" % name
257 raise errors.ProgrammerError("Unknown tag kind")
259 def _VerifyTags(data):
260 AssertEqual(sorted(tags), sorted(data))
263 (uri, _VerifyTags, 'GET', None),
267 def _WaitForRapiJob(job_id):
268 """Waits for a job to finish.
271 master = qa_config.GetMasterNode()
273 def _VerifyJob(data):
274 AssertEqual(data["id"], job_id)
275 for field in JOB_FIELDS:
276 AssertIn(field, data)
279 ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
282 return rapi.client_utils.PollJob(_rapi_client, job_id,
283 cli.StdioJobPollReportCb())
286 def TestRapiInstanceAdd(node, use_client):
287 """Test adding a new instance via RAPI"""
288 instance = qa_config.AcquireInstance()
290 memory = utils.ParseUnit(qa_config.get("mem"))
291 disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
294 disks = [{"size": size} for size in disk_sizes]
298 constants.BE_MEMORY: memory,
301 job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
305 os=qa_config.get("os"),
306 pnode=node["primary"],
310 "name": instance["name"],
311 "os": qa_config.get("os"),
312 "disk_template": constants.DT_PLAIN,
313 "pnode": node["primary"],
318 (job_id, ) = _DoTests([
319 ("/2/instances", _VerifyReturnsJob, "POST", body),
322 _WaitForRapiJob(job_id)
326 qa_config.ReleaseInstance(instance)
330 def TestRapiInstanceRemove(instance, use_client):
331 """Test removing instance via RAPI"""
333 job_id = _rapi_client.DeleteInstance(instance["name"])
335 (job_id, ) = _DoTests([
336 ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
339 _WaitForRapiJob(job_id)
341 qa_config.ReleaseInstance(instance)
344 def TestInterClusterInstanceMove(src_instance, dest_instance, pnode, snode):
345 """Test tools/move-instance"""
346 master = qa_config.GetMasterNode()
348 rapi_pw_file = tempfile.NamedTemporaryFile()
349 rapi_pw_file.write(_rapi_password)
352 # TODO: Run some instance tests before moving back
353 for srcname, destname in [(src_instance["name"], dest_instance["name"]),
354 (dest_instance["name"], src_instance["name"])]:
356 "../tools/move-instance",
358 "--src-ca-file=%s" % _rapi_ca.name,
359 "--src-username=%s" % _rapi_username,
360 "--src-password-file=%s" % rapi_pw_file.name,
361 "--dest-instance-name=%s" % destname,
362 "--dest-primary-node=%s" % pnode["primary"],
363 "--dest-secondary-node=%s" % snode["primary"],
370 AssertEqual(StartLocalCommand(cmd).wait(), 0)