a171121193b7f7c0cc75a160f78db48dd5766fce
[ganeti-local] / qa / qa_rapi.py
1 #
2
3 # Copyright (C) 2007, 2008 Google Inc.
4 #
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.
9 #
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.
14 #
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
18 # 02110-1301, USA.
19
20
21 """Remote API QA tests.
22
23 """
24
25 import tempfile
26
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
33
34 import ganeti.rapi.client
35 import ganeti.rapi.client_utils
36
37 import qa_config
38 import qa_utils
39 import qa_error
40
41 from qa_utils import (AssertEqual, AssertNotEqual, AssertIn, AssertMatch,
42                       StartLocalCommand)
43
44
45 _rapi_ca = None
46 _rapi_client = None
47 _rapi_username = None
48 _rapi_password = None
49
50
51 def Setup(username, password):
52   """Configures the RAPI client.
53
54   """
55   global _rapi_ca
56   global _rapi_client
57   global _rapi_username
58   global _rapi_password
59
60   _rapi_username = username
61   _rapi_password = password
62
63   master = qa_config.GetMasterNode()
64
65   # Load RAPI certificate from master node
66   cmd = ["cat", constants.RAPI_CERT_FILE]
67
68   # Write to temporary file
69   _rapi_ca = tempfile.NamedTemporaryFile()
70   _rapi_ca.write(qa_utils.GetCommandOutput(master["primary"],
71                                            utils.ShellQuoteArgs(cmd)))
72   _rapi_ca.flush()
73
74   port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
75   cfg_ssl = rapi.client.CertAuthorityVerify(cafile=_rapi_ca.name)
76
77   _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
78                                               username=username,
79                                               password=password,
80                                               config_ssl_verification=cfg_ssl,
81                                               ignore_proxy=True)
82
83   print "RAPI protocol version: %s" % _rapi_client.GetVersion()
84
85
86 INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
87                    "admin_state",
88                    "disk_template", "disk.sizes",
89                    "nic.ips", "nic.macs", "nic.modes", "nic.links",
90                    "beparams", "hvparams",
91                    "oper_state", "oper_ram", "status", "tags")
92
93 NODE_FIELDS = ("name", "dtotal", "dfree",
94                "mtotal", "mnode", "mfree",
95                "pinst_cnt", "sinst_cnt", "tags")
96
97 JOB_FIELDS = frozenset([
98   "id", "ops", "status", "summary",
99   "opstatus", "opresult", "oplog",
100   "received_ts", "start_ts", "end_ts",
101   ])
102
103 LIST_FIELDS = ("id", "uri")
104
105
106 def Enabled():
107   """Return whether remote API tests should be run.
108
109   """
110   return qa_config.TestEnabled('rapi')
111
112
113 def _DoTests(uris):
114   results = []
115
116   for uri, verify, method, body in uris:
117     assert uri.startswith("/")
118
119     data = _rapi_client._SendRequest(method, uri, None, body)
120
121     if verify is not None:
122       if callable(verify):
123         verify(data)
124       else:
125         AssertEqual(data, verify)
126
127       results.append(data)
128
129   return results
130
131
132 def _VerifyReturnsJob(data):
133   AssertMatch(data, r'^\d+$')
134
135
136 def TestVersion():
137   """Testing remote API version.
138
139   """
140   _DoTests([
141     ("/version", constants.RAPI_VERSION, 'GET', None),
142     ])
143
144
145 def TestEmptyCluster():
146   """Testing remote API on an empty cluster.
147
148   """
149   master = qa_config.GetMasterNode()
150   master_full = qa_utils.ResolveNodeName(master)
151
152   def _VerifyInfo(data):
153     AssertIn("name", data)
154     AssertIn("master", data)
155     AssertEqual(data["master"], master_full)
156
157   def _VerifyNodes(data):
158     master_entry = {
159       "id": master_full,
160       "uri": "/2/nodes/%s" % master_full,
161       }
162     AssertIn(master_entry, data)
163
164   def _VerifyNodesBulk(data):
165     for node in data:
166       for entry in NODE_FIELDS:
167         AssertIn(entry, node)
168
169   _DoTests([
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),
178     ])
179
180
181 def TestInstance(instance):
182   """Testing getting instance(s) info via remote API.
183
184   """
185   def _VerifyInstance(data):
186     for entry in INSTANCE_FIELDS:
187       AssertIn(entry, data)
188
189   def _VerifyInstancesList(data):
190     for instance in data:
191       for entry in LIST_FIELDS:
192         AssertIn(entry, instance)
193
194   def _VerifyInstancesBulk(data):
195     for instance_data in data:
196       _VerifyInstance(instance_data)
197
198   _DoTests([
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),
206     ])
207
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),
213     ])
214
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"])
220
221
222 def TestNode(node):
223   """Testing getting node(s) info via remote API.
224
225   """
226   def _VerifyNode(data):
227     for entry in NODE_FIELDS:
228       AssertIn(entry, data)
229
230   def _VerifyNodesList(data):
231     for node in data:
232       for entry in LIST_FIELDS:
233         AssertIn(entry, node)
234
235   def _VerifyNodesBulk(data):
236     for node_data in data:
237       _VerifyNode(node_data)
238
239   _DoTests([
240     ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET', None),
241     ("/2/nodes", _VerifyNodesList, 'GET', None),
242     ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
243     ])
244
245
246 def TestTags(kind, name, tags):
247   """Tests .../tags resources.
248
249   """
250   if kind == constants.TAG_CLUSTER:
251     uri = "/2/tags"
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
256   else:
257     raise errors.ProgrammerError("Unknown tag kind")
258
259   def _VerifyTags(data):
260     AssertEqual(sorted(tags), sorted(data))
261
262   _DoTests([
263     (uri, _VerifyTags, 'GET', None),
264     ])
265
266
267 def _WaitForRapiJob(job_id):
268   """Waits for a job to finish.
269
270   """
271   master = qa_config.GetMasterNode()
272
273   def _VerifyJob(data):
274     AssertEqual(data["id"], job_id)
275     for field in JOB_FIELDS:
276       AssertIn(field, data)
277
278   _DoTests([
279     ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
280     ])
281
282   return rapi.client_utils.PollJob(_rapi_client, job_id,
283                                    cli.StdioJobPollReportCb())
284
285
286 def TestRapiInstanceAdd(node, use_client):
287   """Test adding a new instance via RAPI"""
288   instance = qa_config.AcquireInstance()
289   try:
290     memory = utils.ParseUnit(qa_config.get("mem"))
291     disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
292
293     if use_client:
294       disks = [{"size": size} for size in disk_sizes]
295       nics = [{}]
296
297       beparams = {
298         constants.BE_MEMORY: memory,
299         }
300
301       job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
302                                            instance["name"],
303                                            constants.DT_PLAIN,
304                                            disks, nics,
305                                            os=qa_config.get("os"),
306                                            pnode=node["primary"],
307                                            beparams=beparams)
308     else:
309       body = {
310         "name": instance["name"],
311         "os": qa_config.get("os"),
312         "disk_template": constants.DT_PLAIN,
313         "pnode": node["primary"],
314         "memory": memory,
315         "disks": disk_sizes,
316         }
317
318       (job_id, ) = _DoTests([
319         ("/2/instances", _VerifyReturnsJob, "POST", body),
320         ])
321
322     _WaitForRapiJob(job_id)
323
324     return instance
325   except:
326     qa_config.ReleaseInstance(instance)
327     raise
328
329
330 def TestRapiInstanceRemove(instance, use_client):
331   """Test removing instance via RAPI"""
332   if use_client:
333     job_id = _rapi_client.DeleteInstance(instance["name"])
334   else:
335     (job_id, ) = _DoTests([
336       ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
337       ])
338
339   _WaitForRapiJob(job_id)
340
341   qa_config.ReleaseInstance(instance)
342
343
344 def TestInterClusterInstanceMove(src_instance, dest_instance, pnode, snode):
345   """Test tools/move-instance"""
346   master = qa_config.GetMasterNode()
347
348   rapi_pw_file = tempfile.NamedTemporaryFile()
349   rapi_pw_file.write(_rapi_password)
350   rapi_pw_file.flush()
351
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"])]:
355     cmd = [
356       "../tools/move-instance",
357       "--verbose",
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"],
364
365       master["primary"],
366       master["primary"],
367       srcname,
368       ]
369
370     AssertEqual(StartLocalCommand(cmd).wait(), 0)