RAPI QA: Use RAPI client
[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                       StartSSH)
43
44
45 _rapi_ca = None
46 _rapi_client = None
47
48
49 def Setup(username, password):
50   """Configures the RAPI client.
51
52   """
53   global _rapi_ca
54   global _rapi_client
55
56   master = qa_config.GetMasterNode()
57
58   # Load RAPI certificate from master node
59   cmd = ["cat", constants.RAPI_CERT_FILE]
60
61   # Write to temporary file
62   _rapi_ca = tempfile.NamedTemporaryFile()
63   _rapi_ca.write(qa_utils.GetCommandOutput(master["primary"],
64                                            utils.ShellQuoteArgs(cmd)))
65   _rapi_ca.flush()
66
67   port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
68   cfg_ssl = rapi.client.CertAuthorityVerify(cafile=_rapi_ca.name)
69
70   _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
71                                               username=username,
72                                               password=password,
73                                               config_ssl_verification=cfg_ssl,
74                                               ignore_proxy=True)
75
76   print "RAPI protocol version: %s" % _rapi_client.GetVersion()
77
78
79 INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
80                    "admin_state",
81                    "disk_template", "disk.sizes",
82                    "nic.ips", "nic.macs", "nic.modes", "nic.links",
83                    "beparams", "hvparams",
84                    "oper_state", "oper_ram", "status", "tags")
85
86 NODE_FIELDS = ("name", "dtotal", "dfree",
87                "mtotal", "mnode", "mfree",
88                "pinst_cnt", "sinst_cnt", "tags")
89
90 JOB_FIELDS = frozenset([
91   "id", "ops", "status", "summary",
92   "opstatus", "opresult", "oplog",
93   "received_ts", "start_ts", "end_ts",
94   ])
95
96 LIST_FIELDS = ("id", "uri")
97
98
99 def Enabled():
100   """Return whether remote API tests should be run.
101
102   """
103   return qa_config.TestEnabled('rapi')
104
105
106 def _DoTests(uris):
107   results = []
108
109   for uri, verify, method, body in uris:
110     assert uri.startswith("/")
111
112     data = _rapi_client._SendRequest(method, uri, None, body)
113
114     if verify is not None:
115       if callable(verify):
116         verify(data)
117       else:
118         AssertEqual(data, verify)
119
120       results.append(data)
121
122   return results
123
124
125 def _VerifyReturnsJob(data):
126   AssertMatch(data, r'^\d+$')
127
128
129 def TestVersion():
130   """Testing remote API version.
131
132   """
133   _DoTests([
134     ("/version", constants.RAPI_VERSION, 'GET', None),
135     ])
136
137
138 def TestEmptyCluster():
139   """Testing remote API on an empty cluster.
140
141   """
142   master = qa_config.GetMasterNode()
143   master_full = qa_utils.ResolveNodeName(master)
144
145   def _VerifyInfo(data):
146     AssertIn("name", data)
147     AssertIn("master", data)
148     AssertEqual(data["master"], master_full)
149
150   def _VerifyNodes(data):
151     master_entry = {
152       "id": master_full,
153       "uri": "/2/nodes/%s" % master_full,
154       }
155     AssertIn(master_entry, data)
156
157   def _VerifyNodesBulk(data):
158     for node in data:
159       for entry in NODE_FIELDS:
160         AssertIn(entry, node)
161
162   _DoTests([
163     ("/", None, 'GET', None),
164     ("/2/info", _VerifyInfo, 'GET', None),
165     ("/2/tags", None, 'GET', None),
166     ("/2/nodes", _VerifyNodes, 'GET', None),
167     ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
168     ("/2/instances", [], 'GET', None),
169     ("/2/instances?bulk=1", [], 'GET', None),
170     ("/2/os", None, 'GET', None),
171     ])
172
173
174 def TestInstance(instance):
175   """Testing getting instance(s) info via remote API.
176
177   """
178   def _VerifyInstance(data):
179     for entry in INSTANCE_FIELDS:
180       AssertIn(entry, data)
181
182   def _VerifyInstancesList(data):
183     for instance in data:
184       for entry in LIST_FIELDS:
185         AssertIn(entry, instance)
186
187   def _VerifyInstancesBulk(data):
188     for instance_data in data:
189       _VerifyInstance(instance_data)
190
191   _DoTests([
192     ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET', None),
193     ("/2/instances", _VerifyInstancesList, 'GET', None),
194     ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET', None),
195     ("/2/instances/%s/activate-disks" % instance["name"],
196      _VerifyReturnsJob, 'PUT', None),
197     ("/2/instances/%s/deactivate-disks" % instance["name"],
198      _VerifyReturnsJob, 'PUT', None),
199     ])
200
201
202 def TestNode(node):
203   """Testing getting node(s) info via remote API.
204
205   """
206   def _VerifyNode(data):
207     for entry in NODE_FIELDS:
208       AssertIn(entry, data)
209
210   def _VerifyNodesList(data):
211     for node in data:
212       for entry in LIST_FIELDS:
213         AssertIn(entry, node)
214
215   def _VerifyNodesBulk(data):
216     for node_data in data:
217       _VerifyNode(node_data)
218
219   _DoTests([
220     ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET', None),
221     ("/2/nodes", _VerifyNodesList, 'GET', None),
222     ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
223     ])
224
225
226 def TestTags(kind, name, tags):
227   """Tests .../tags resources.
228
229   """
230   if kind == constants.TAG_CLUSTER:
231     uri = "/2/tags"
232   elif kind == constants.TAG_NODE:
233     uri = "/2/nodes/%s/tags" % name
234   elif kind == constants.TAG_INSTANCE:
235     uri = "/2/instances/%s/tags" % name
236   else:
237     raise errors.ProgrammerError("Unknown tag kind")
238
239   def _VerifyTags(data):
240     AssertEqual(sorted(tags), sorted(data))
241
242   _DoTests([
243     (uri, _VerifyTags, 'GET', None),
244     ])
245
246
247 def _WaitForRapiJob(job_id):
248   """Waits for a job to finish.
249
250   """
251   master = qa_config.GetMasterNode()
252
253   def _VerifyJob(data):
254     AssertEqual(data["id"], job_id)
255     for field in JOB_FIELDS:
256       AssertIn(field, data)
257
258   _DoTests([
259     ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
260     ])
261
262   rapi.client_utils.PollJob(_rapi_client, job_id, cli.StdioJobPollReportCb())
263
264
265 def TestRapiInstanceAdd(node):
266   """Test adding a new instance via RAPI"""
267   instance = qa_config.AcquireInstance()
268   try:
269     body = {
270       "name": instance["name"],
271       "os": qa_config.get("os"),
272       "disk_template": constants.DT_PLAIN,
273       "pnode": node["primary"],
274       "memory": utils.ParseUnit(qa_config.get("mem")),
275       "disks": [utils.ParseUnit(size) for size in qa_config.get("disk")],
276       }
277
278     (job_id, ) = _DoTests([
279       ("/2/instances", _VerifyReturnsJob, "POST", body),
280       ])
281
282     _WaitForRapiJob(job_id)
283
284     return instance
285   except:
286     qa_config.ReleaseInstance(instance)
287     raise
288
289
290 def TestRapiInstanceRemove(instance):
291   """Test removing instance via RAPI"""
292   (job_id, ) = _DoTests([
293     ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
294     ])
295
296   _WaitForRapiJob(job_id)
297
298   qa_config.ReleaseInstance(instance)