Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ 3b7158ef

History | View | Annotate | Download (10.7 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 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_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
76
                                           proxy="")
77

    
78
  _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
79
                                              username=username,
80
                                              password=password,
81
                                              curl_config_fn=cfg_curl)
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", "oper_vcpus", "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 TestRapiInstanceMigrate(instance):
345
  """Test migrating instance via RAPI"""
346
  # Move to secondary node
347
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
348
  # And back to previous primary
349
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
350

    
351

    
352
def TestRapiInstanceRename(instance, rename_target):
353
  """Test renaming instance via RAPI"""
354
  rename_source = instance["name"]
355

    
356
  for name1, name2 in [(rename_source, rename_target),
357
                       (rename_target, rename_source)]:
358
    _WaitForRapiJob(_rapi_client.RenameInstance(name1, name2))
359

    
360

    
361
def TestRapiInstanceModify(instance):
362
  """Test modifying instance via RAPI"""
363
  def _ModifyInstance(**kwargs):
364
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
365

    
366
  _ModifyInstance(hvparams={
367
    constants.HV_KERNEL_ARGS: "single",
368
    })
369

    
370
  _ModifyInstance(beparams={
371
    constants.BE_VCPUS: 3,
372
    })
373

    
374
  _ModifyInstance(beparams={
375
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
376
    })
377

    
378
  _ModifyInstance(hvparams={
379
    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
380
    })
381

    
382

    
383
def TestInterClusterInstanceMove(src_instance, dest_instance, pnode, snode):
384
  """Test tools/move-instance"""
385
  master = qa_config.GetMasterNode()
386

    
387
  rapi_pw_file = tempfile.NamedTemporaryFile()
388
  rapi_pw_file.write(_rapi_password)
389
  rapi_pw_file.flush()
390

    
391
  # TODO: Run some instance tests before moving back
392
  for srcname, destname in [(src_instance["name"], dest_instance["name"]),
393
                            (dest_instance["name"], src_instance["name"])]:
394
    cmd = [
395
      "../tools/move-instance",
396
      "--verbose",
397
      "--src-ca-file=%s" % _rapi_ca.name,
398
      "--src-username=%s" % _rapi_username,
399
      "--src-password-file=%s" % rapi_pw_file.name,
400
      "--dest-instance-name=%s" % destname,
401
      "--dest-primary-node=%s" % pnode["primary"],
402
      "--dest-secondary-node=%s" % snode["primary"],
403

    
404
      master["primary"],
405
      master["primary"],
406
      srcname,
407
      ]
408

    
409
    AssertEqual(StartLocalCommand(cmd).wait(), 0)