Adding QA RAPI tests for activate-disks and deactivate-disks calls
[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 urllib2
26
27 from ganeti import utils
28 from ganeti import constants
29 from ganeti import errors
30 from ganeti import serializer
31
32 import qa_config
33 import qa_utils
34 import qa_error
35
36 from qa_utils import (AssertEqual, AssertNotEqual, AssertIn, AssertMatch,
37                       StartSSH)
38
39
40 class OpenerFactory:
41   """A factory singleton to construct urllib opener chain.
42
43   This is needed because qa_config is not initialized yet at module load time
44
45   """
46   _opener = None
47
48   @classmethod
49   def Opener(cls):
50     """Construct the opener if not yet done.
51
52     """
53     if not cls._opener:
54       # Create opener which doesn't try to look for proxies and does auth
55       master = qa_config.GetMasterNode()
56       host = master["primary"]
57       port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
58       passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
59       passman.add_password(None, 'https://%s:%s' % (host, port),
60                            qa_config.get("rapi-user", default=""),
61                            qa_config.get("rapi-pass", default=""))
62       authhandler = urllib2.HTTPBasicAuthHandler(passman)
63       cls._opener = urllib2.build_opener(urllib2.ProxyHandler({}), authhandler)
64
65     return cls._opener
66
67
68 class RapiRequest(urllib2.Request):
69   """This class supports other methods beside GET/POST.
70
71   """
72
73   def __init__(self, url, data=None, headers={}, origin_req_host=None,
74                unverifiable=False, method="GET"):
75     urllib2.Request.__init__(self, url, data, headers, origin_req_host,
76                              unverifiable)
77     self._method = method
78
79   def get_method(self):
80     return self._method
81
82
83 INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
84                    "admin_state",
85                    "disk_template", "disk.sizes",
86                    "nic.ips", "nic.macs", "nic.modes", "nic.links",
87                    "beparams", "hvparams",
88                    "oper_state", "oper_ram", "status", "tags")
89
90 NODE_FIELDS = ("name", "dtotal", "dfree",
91                "mtotal", "mnode", "mfree",
92                "pinst_cnt", "sinst_cnt", "tags")
93
94 LIST_FIELDS = ("id", "uri")
95
96
97 def Enabled():
98   """Return whether remote API tests should be run.
99
100   """
101   return qa_config.TestEnabled('rapi')
102
103
104 def _DoTests(uris):
105   master = qa_config.GetMasterNode()
106   host = master["primary"]
107   port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
108
109   for uri, verify, method in uris:
110     assert uri.startswith("/")
111
112     url = "https://%s:%s%s" % (host, port, uri)
113
114     print "Testing %s ..." % url
115
116     req = RapiRequest(url, method=method)
117     response = OpenerFactory.Opener().open(req)
118
119     AssertEqual(response.info()["Content-type"], "application/json")
120
121     data = serializer.LoadJson(response.read())
122
123     if verify is not None:
124       if callable(verify):
125         verify(data)
126       else:
127         AssertEqual(data, verify)
128
129
130 def TestVersion():
131   """Testing remote API version.
132
133   """
134   _DoTests([
135     ("/version", constants.RAPI_VERSION, 'GET'),
136     ])
137
138
139 def TestEmptyCluster():
140   """Testing remote API on an empty cluster.
141
142   """
143   master_name = qa_config.GetMasterNode()["primary"]
144
145   def _VerifyInfo(data):
146     AssertIn("name", data)
147     AssertIn("master", data)
148     AssertEqual(data["master"], master_name)
149
150   def _VerifyNodes(data):
151     master_entry = {
152       "id": master_name,
153       "uri": "/2/nodes/%s" % master_name,
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'),
164     ("/2/info", _VerifyInfo, 'GET'),
165     ("/2/tags", None, 'GET'),
166     ("/2/nodes", _VerifyNodes, 'GET'),
167     ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET'),
168     ("/2/instances", [], 'GET'),
169     ("/2/instances?bulk=1", [], 'GET'),
170     ("/2/os", None, 'GET'),
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   def _VerifyReturnsJob(data):
192     AssertMatch(data, r'^\d+$')
193
194   _DoTests([
195     ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET'),
196     ("/2/instances", _VerifyInstancesList, 'GET'),
197     ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET'),
198     ("/2/instances/%s/activate-disks" % instance["name"], _VerifyReturnsJob, 'PUT'),
199     ("/2/instances/%s/deactivate-disks" % instance["name"], _VerifyReturnsJob, 'PUT'),
200     ])
201
202
203 def TestNode(node):
204   """Testing getting node(s) info via remote API.
205
206   """
207   def _VerifyNode(data):
208     for entry in NODE_FIELDS:
209       AssertIn(entry, data)
210
211   def _VerifyNodesList(data):
212     for node in data:
213       for entry in LIST_FIELDS:
214         AssertIn(entry, node)
215
216   def _VerifyNodesBulk(data):
217     for node_data in data:
218       _VerifyNode(node_data)
219
220   _DoTests([
221     ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET'),
222     ("/2/nodes", _VerifyNodesList, 'GET'),
223     ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET'),
224     ])
225
226
227 def TestTags(kind, name, tags):
228   """Tests .../tags resources.
229
230   """
231   if kind == constants.TAG_CLUSTER:
232     uri = "/2/tags"
233   elif kind == constants.TAG_NODE:
234     uri = "/2/nodes/%s/tags" % name
235   elif kind == constants.TAG_INSTANCE:
236     uri = "/2/instances/%s/tags" % name
237   else:
238     raise errors.ProgrammerError("Unknown tag kind")
239
240   def _VerifyTags(data):
241     # Create copies to modify
242     should = tags[:]
243     should.sort()
244
245     returned = data[:]
246     returned.sort()
247     AssertEqual(should, returned)
248
249   _DoTests([
250     (uri, _VerifyTags, 'GET'),
251     ])