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
36 from qa_utils import (AssertEqual, AssertNotEqual, AssertIn, AssertMatch,
41 """A factory singleton to construct urllib opener chain.
43 This is needed because qa_config is not initialized yet at module load time
51 def SetCredentials(cls, rapi_user, rapi_secret):
52 """Set the credentials for authorized access.
55 cls._rapi_user = rapi_user
56 cls._rapi_secret = rapi_secret
60 """Construct the opener if not yet done.
64 if not cls._rapi_user or not cls._rapi_secret:
65 raise errors.ProgrammerError("SetCredentials was never called.")
67 # Create opener which doesn't try to look for proxies and does auth
68 master = qa_config.GetMasterNode()
69 host = master["primary"]
70 port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
71 passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
72 passman.add_password(None, 'https://%s:%s' % (host, port),
75 authhandler = urllib2.HTTPBasicAuthHandler(passman)
76 cls._opener = urllib2.build_opener(urllib2.ProxyHandler({}), authhandler)
81 class RapiRequest(urllib2.Request):
82 """This class supports other methods beside GET/POST.
86 def __init__(self, url, data=None, headers={}, origin_req_host=None,
87 unverifiable=False, method="GET"):
88 urllib2.Request.__init__(self, url, data, headers, origin_req_host,
96 INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
98 "disk_template", "disk.sizes",
99 "nic.ips", "nic.macs", "nic.modes", "nic.links",
100 "beparams", "hvparams",
101 "oper_state", "oper_ram", "status", "tags")
103 NODE_FIELDS = ("name", "dtotal", "dfree",
104 "mtotal", "mnode", "mfree",
105 "pinst_cnt", "sinst_cnt", "tags")
107 LIST_FIELDS = ("id", "uri")
111 """Return whether remote API tests should be run.
114 return qa_config.TestEnabled('rapi')
118 master = qa_config.GetMasterNode()
119 host = master["primary"]
120 port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
122 for uri, verify, method in uris:
123 assert uri.startswith("/")
125 url = "https://%s:%s%s" % (host, port, uri)
127 print "Testing %s ..." % url
129 req = RapiRequest(url, method=method)
130 response = OpenerFactory.Opener().open(req)
132 AssertEqual(response.info()["Content-type"], "application/json")
134 data = serializer.LoadJson(response.read())
136 if verify is not None:
140 AssertEqual(data, verify)
144 """Testing remote API version.
148 ("/version", constants.RAPI_VERSION, 'GET'),
152 def TestEmptyCluster():
153 """Testing remote API on an empty cluster.
156 master_name = qa_config.GetMasterNode()["primary"]
158 def _VerifyInfo(data):
159 AssertIn("name", data)
160 AssertIn("master", data)
161 AssertEqual(data["master"], master_name)
163 def _VerifyNodes(data):
166 "uri": "/2/nodes/%s" % master_name,
168 AssertIn(master_entry, data)
170 def _VerifyNodesBulk(data):
172 for entry in NODE_FIELDS:
173 AssertIn(entry, node)
177 ("/2/info", _VerifyInfo, 'GET'),
178 ("/2/tags", None, 'GET'),
179 ("/2/nodes", _VerifyNodes, 'GET'),
180 ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET'),
181 ("/2/instances", [], 'GET'),
182 ("/2/instances?bulk=1", [], 'GET'),
183 ("/2/os", None, 'GET'),
187 def TestInstance(instance):
188 """Testing getting instance(s) info via remote API.
191 def _VerifyInstance(data):
192 for entry in INSTANCE_FIELDS:
193 AssertIn(entry, data)
195 def _VerifyInstancesList(data):
196 for instance in data:
197 for entry in LIST_FIELDS:
198 AssertIn(entry, instance)
200 def _VerifyInstancesBulk(data):
201 for instance_data in data:
202 _VerifyInstance(instance_data)
204 def _VerifyReturnsJob(data):
205 AssertMatch(data, r'^\d+$')
208 ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET'),
209 ("/2/instances", _VerifyInstancesList, 'GET'),
210 ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET'),
211 ("/2/instances/%s/activate-disks" % instance["name"], _VerifyReturnsJob, 'PUT'),
212 ("/2/instances/%s/deactivate-disks" % instance["name"], _VerifyReturnsJob, 'PUT'),
217 """Testing getting node(s) info via remote API.
220 def _VerifyNode(data):
221 for entry in NODE_FIELDS:
222 AssertIn(entry, data)
224 def _VerifyNodesList(data):
226 for entry in LIST_FIELDS:
227 AssertIn(entry, node)
229 def _VerifyNodesBulk(data):
230 for node_data in data:
231 _VerifyNode(node_data)
234 ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET'),
235 ("/2/nodes", _VerifyNodesList, 'GET'),
236 ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET'),
240 def TestTags(kind, name, tags):
241 """Tests .../tags resources.
244 if kind == constants.TAG_CLUSTER:
246 elif kind == constants.TAG_NODE:
247 uri = "/2/nodes/%s/tags" % name
248 elif kind == constants.TAG_INSTANCE:
249 uri = "/2/instances/%s/tags" % name
251 raise errors.ProgrammerError("Unknown tag kind")
253 def _VerifyTags(data):
254 # Create copies to modify
260 AssertEqual(should, returned)
263 (uri, _VerifyTags, 'GET'),