Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 94e63ca1

History | View | Annotate | Download (7.3 kB)

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
  _rapi_user = None
48
  _rapi_secret = None
49

    
50
  @classmethod
51
  def SetCredentials(cls, rapi_user, rapi_secret):
52
    """Set the credentials for authorized access.
53

54
    """
55
    cls._rapi_user = rapi_user
56
    cls._rapi_secret = rapi_secret
57

    
58
  @classmethod
59
  def Opener(cls):
60
    """Construct the opener if not yet done.
61

62
    """
63
    if not cls._opener:
64
      if not cls._rapi_user or not cls._rapi_secret:
65
        raise errors.ProgrammerError("SetCredentials was never called.")
66

    
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),
73
                           cls._rapi_user,
74
                           cls._rapi_secret)
75
      authhandler = urllib2.HTTPBasicAuthHandler(passman)
76
      cls._opener = urllib2.build_opener(urllib2.ProxyHandler({}), authhandler)
77

    
78
    return cls._opener
79

    
80

    
81
class RapiRequest(urllib2.Request):
82
  """This class supports other methods beside GET/POST.
83

84
  """
85

    
86
  def __init__(self, method, url, headers, data):
87
    urllib2.Request.__init__(self, url, data=data, headers=headers)
88
    self._method = method
89

    
90
  def get_method(self):
91
    return self._method
92

    
93

    
94
INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
95
                   "admin_state",
96
                   "disk_template", "disk.sizes",
97
                   "nic.ips", "nic.macs", "nic.modes", "nic.links",
98
                   "beparams", "hvparams",
99
                   "oper_state", "oper_ram", "status", "tags")
100

    
101
NODE_FIELDS = ("name", "dtotal", "dfree",
102
               "mtotal", "mnode", "mfree",
103
               "pinst_cnt", "sinst_cnt", "tags")
104

    
105
JOB_FIELDS = frozenset([
106
  "id", "ops", "status", "summary",
107
  "opstatus", "opresult", "oplog",
108
  "received_ts", "start_ts", "end_ts",
109
  ])
110

    
111
LIST_FIELDS = ("id", "uri")
112

    
113

    
114
def Enabled():
115
  """Return whether remote API tests should be run.
116

117
  """
118
  return qa_config.TestEnabled('rapi')
119

    
120

    
121
def _DoTests(uris):
122
  master = qa_config.GetMasterNode()
123
  host = master["primary"]
124
  port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
125
  results = []
126

    
127
  for uri, verify, method, body in uris:
128
    assert uri.startswith("/")
129

    
130
    url = "https://%s:%s%s" % (host, port, uri)
131

    
132
    headers = {}
133

    
134
    if body:
135
      data = serializer.DumpJson(body, indent=False)
136
      headers["Content-Type"] = "application/json"
137
    else:
138
      data = None
139

    
140
    if headers or data:
141
      details = []
142
      if headers:
143
        details.append("headers=%s" %
144
                       serializer.DumpJson(headers, indent=False).rstrip())
145
      if data:
146
        details.append("data=%s" % data.rstrip())
147
      info = "(%s)" % (", ".join(details), )
148
    else:
149
      info = ""
150

    
151
    print "Testing %s %s %s..." % (method, url, info)
152

    
153
    req = RapiRequest(method, url, headers, data)
154
    response = OpenerFactory.Opener().open(req)
155

    
156
    AssertEqual(response.info()["Content-type"], "application/json")
157

    
158
    data = serializer.LoadJson(response.read())
159

    
160
    if verify is not None:
161
      if callable(verify):
162
        verify(data)
163
      else:
164
        AssertEqual(data, verify)
165

    
166
      results.append(data)
167

    
168
  return results
169

    
170

    
171
def _VerifyReturnsJob(data):
172
  AssertMatch(data, r'^\d+$')
173

    
174

    
175
def TestVersion():
176
  """Testing remote API version.
177

178
  """
179
  _DoTests([
180
    ("/version", constants.RAPI_VERSION, 'GET', None),
181
    ])
182

    
183

    
184
def TestEmptyCluster():
185
  """Testing remote API on an empty cluster.
186

187
  """
188
  master = qa_config.GetMasterNode()
189
  master_full = qa_utils.ResolveNodeName(master)
190

    
191
  def _VerifyInfo(data):
192
    AssertIn("name", data)
193
    AssertIn("master", data)
194
    AssertEqual(data["master"], master_full)
195

    
196
  def _VerifyNodes(data):
197
    master_entry = {
198
      "id": master_full,
199
      "uri": "/2/nodes/%s" % master_full,
200
      }
201
    AssertIn(master_entry, data)
202

    
203
  def _VerifyNodesBulk(data):
204
    for node in data:
205
      for entry in NODE_FIELDS:
206
        AssertIn(entry, node)
207

    
208
  _DoTests([
209
    ("/", None, 'GET', None),
210
    ("/2/info", _VerifyInfo, 'GET', None),
211
    ("/2/tags", None, 'GET', None),
212
    ("/2/nodes", _VerifyNodes, 'GET', None),
213
    ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
214
    ("/2/instances", [], 'GET', None),
215
    ("/2/instances?bulk=1", [], 'GET', None),
216
    ("/2/os", None, 'GET', None),
217
    ])
218

    
219

    
220
def TestInstance(instance):
221
  """Testing getting instance(s) info via remote API.
222

223
  """
224
  def _VerifyInstance(data):
225
    for entry in INSTANCE_FIELDS:
226
      AssertIn(entry, data)
227

    
228
  def _VerifyInstancesList(data):
229
    for instance in data:
230
      for entry in LIST_FIELDS:
231
        AssertIn(entry, instance)
232

    
233
  def _VerifyInstancesBulk(data):
234
    for instance_data in data:
235
      _VerifyInstance(instance_data)
236

    
237
  _DoTests([
238
    ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET', None),
239
    ("/2/instances", _VerifyInstancesList, 'GET', None),
240
    ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET', None),
241
    ("/2/instances/%s/activate-disks" % instance["name"],
242
     _VerifyReturnsJob, 'PUT', None),
243
    ("/2/instances/%s/deactivate-disks" % instance["name"],
244
     _VerifyReturnsJob, 'PUT', None),
245
    ])
246

    
247

    
248
def TestNode(node):
249
  """Testing getting node(s) info via remote API.
250

251
  """
252
  def _VerifyNode(data):
253
    for entry in NODE_FIELDS:
254
      AssertIn(entry, data)
255

    
256
  def _VerifyNodesList(data):
257
    for node in data:
258
      for entry in LIST_FIELDS:
259
        AssertIn(entry, node)
260

    
261
  def _VerifyNodesBulk(data):
262
    for node_data in data:
263
      _VerifyNode(node_data)
264

    
265
  _DoTests([
266
    ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET', None),
267
    ("/2/nodes", _VerifyNodesList, 'GET', None),
268
    ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
269
    ])
270

    
271

    
272
def TestTags(kind, name, tags):
273
  """Tests .../tags resources.
274

275
  """
276
  if kind == constants.TAG_CLUSTER:
277
    uri = "/2/tags"
278
  elif kind == constants.TAG_NODE:
279
    uri = "/2/nodes/%s/tags" % name
280
  elif kind == constants.TAG_INSTANCE:
281
    uri = "/2/instances/%s/tags" % name
282
  else:
283
    raise errors.ProgrammerError("Unknown tag kind")
284

    
285
  def _VerifyTags(data):
286
    AssertEqual(sorted(tags), sorted(data))
287

    
288
  _DoTests([
289
    (uri, _VerifyTags, 'GET', None),
290
    ])