Statistics
| Branch: | Tag: | Revision:

root / qa / qa_rapi.py @ c326b4ef

History | View | Annotate | Download (11.1 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
    print "%s %s" % (method, uri)
120
    data = _rapi_client._SendRequest(method, uri, None, body)
121

    
122
    if verify is not None:
123
      if callable(verify):
124
        verify(data)
125
      else:
126
        AssertEqual(data, verify)
127

    
128
    results.append(data)
129

    
130
  return results
131

    
132

    
133
def _VerifyReturnsJob(data):
134
  AssertMatch(data, r'^\d+$')
135

    
136

    
137
def TestVersion():
138
  """Testing remote API version.
139

140
  """
141
  _DoTests([
142
    ("/version", constants.RAPI_VERSION, 'GET', None),
143
    ])
144

    
145

    
146
def TestEmptyCluster():
147
  """Testing remote API on an empty cluster.
148

149
  """
150
  master = qa_config.GetMasterNode()
151
  master_full = qa_utils.ResolveNodeName(master)
152

    
153
  def _VerifyInfo(data):
154
    AssertIn("name", data)
155
    AssertIn("master", data)
156
    AssertEqual(data["master"], master_full)
157

    
158
  def _VerifyNodes(data):
159
    master_entry = {
160
      "id": master_full,
161
      "uri": "/2/nodes/%s" % master_full,
162
      }
163
    AssertIn(master_entry, data)
164

    
165
  def _VerifyNodesBulk(data):
166
    for node in data:
167
      for entry in NODE_FIELDS:
168
        AssertIn(entry, node)
169

    
170
  _DoTests([
171
    ("/", None, 'GET', None),
172
    ("/2/info", _VerifyInfo, 'GET', None),
173
    ("/2/tags", None, 'GET', None),
174
    ("/2/nodes", _VerifyNodes, 'GET', None),
175
    ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
176
    ("/2/instances", [], 'GET', None),
177
    ("/2/instances?bulk=1", [], 'GET', None),
178
    ("/2/os", None, 'GET', None),
179
    ])
180

    
181

    
182
def TestInstance(instance):
183
  """Testing getting instance(s) info via remote API.
184

185
  """
186
  def _VerifyInstance(data):
187
    for entry in INSTANCE_FIELDS:
188
      AssertIn(entry, data)
189

    
190
  def _VerifyInstancesList(data):
191
    for instance in data:
192
      for entry in LIST_FIELDS:
193
        AssertIn(entry, instance)
194

    
195
  def _VerifyInstancesBulk(data):
196
    for instance_data in data:
197
      _VerifyInstance(instance_data)
198

    
199
  _DoTests([
200
    ("/2/instances/%s" % instance["name"], _VerifyInstance, 'GET', None),
201
    ("/2/instances", _VerifyInstancesList, 'GET', None),
202
    ("/2/instances?bulk=1", _VerifyInstancesBulk, 'GET', None),
203
    ("/2/instances/%s/activate-disks" % instance["name"],
204
     _VerifyReturnsJob, 'PUT', None),
205
    ("/2/instances/%s/deactivate-disks" % instance["name"],
206
     _VerifyReturnsJob, 'PUT', None),
207
    ])
208

    
209
  # Test OpPrepareExport
210
  (job_id, ) = _DoTests([
211
    ("/2/instances/%s/prepare-export?mode=%s" %
212
     (instance["name"], constants.EXPORT_MODE_REMOTE),
213
     _VerifyReturnsJob, "PUT", None),
214
    ])
215

    
216
  result = _WaitForRapiJob(job_id)[0]
217
  AssertEqual(len(result["handshake"]), 3)
218
  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
219
  AssertEqual(len(result["x509_key_name"]), 3)
220
  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
221

    
222

    
223
def TestNode(node):
224
  """Testing getting node(s) info via remote API.
225

226
  """
227
  def _VerifyNode(data):
228
    for entry in NODE_FIELDS:
229
      AssertIn(entry, data)
230

    
231
  def _VerifyNodesList(data):
232
    for node in data:
233
      for entry in LIST_FIELDS:
234
        AssertIn(entry, node)
235

    
236
  def _VerifyNodesBulk(data):
237
    for node_data in data:
238
      _VerifyNode(node_data)
239

    
240
  _DoTests([
241
    ("/2/nodes/%s" % node["primary"], _VerifyNode, 'GET', None),
242
    ("/2/nodes", _VerifyNodesList, 'GET', None),
243
    ("/2/nodes?bulk=1", _VerifyNodesBulk, 'GET', None),
244
    ])
245

    
246

    
247
def TestTags(kind, name, tags):
248
  """Tests .../tags resources.
249

250
  """
251
  if kind == constants.TAG_CLUSTER:
252
    uri = "/2/tags"
253
  elif kind == constants.TAG_NODE:
254
    uri = "/2/nodes/%s/tags" % name
255
  elif kind == constants.TAG_INSTANCE:
256
    uri = "/2/instances/%s/tags" % name
257
  else:
258
    raise errors.ProgrammerError("Unknown tag kind")
259

    
260
  def _VerifyTags(data):
261
    AssertEqual(sorted(tags), sorted(data))
262

    
263
  query = "&".join("tag=%s" % i for i in tags)
264

    
265
  # Add tags
266
  (job_id, ) = _DoTests([
267
    ("%s?%s" % (uri, query), _VerifyReturnsJob, "PUT", None),
268
    ])
269
  _WaitForRapiJob(job_id)
270

    
271
  # Retrieve tags
272
  _DoTests([
273
    (uri, _VerifyTags, 'GET', None),
274
    ])
275

    
276
  # Remove tags
277
  (job_id, ) = _DoTests([
278
    ("%s?%s" % (uri, query), _VerifyReturnsJob, "DELETE", None),
279
    ])
280
  _WaitForRapiJob(job_id)
281

    
282

    
283
def _WaitForRapiJob(job_id):
284
  """Waits for a job to finish.
285

286
  """
287
  master = qa_config.GetMasterNode()
288

    
289
  def _VerifyJob(data):
290
    AssertEqual(data["id"], job_id)
291
    for field in JOB_FIELDS:
292
      AssertIn(field, data)
293

    
294
  _DoTests([
295
    ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
296
    ])
297

    
298
  return rapi.client_utils.PollJob(_rapi_client, job_id,
299
                                   cli.StdioJobPollReportCb())
300

    
301

    
302
def TestRapiInstanceAdd(node, use_client):
303
  """Test adding a new instance via RAPI"""
304
  instance = qa_config.AcquireInstance()
305
  try:
306
    memory = utils.ParseUnit(qa_config.get("mem"))
307
    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
308

    
309
    if use_client:
310
      disks = [{"size": size} for size in disk_sizes]
311
      nics = [{}]
312

    
313
      beparams = {
314
        constants.BE_MEMORY: memory,
315
        }
316

    
317
      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
318
                                           instance["name"],
319
                                           constants.DT_PLAIN,
320
                                           disks, nics,
321
                                           os=qa_config.get("os"),
322
                                           pnode=node["primary"],
323
                                           beparams=beparams)
324
    else:
325
      body = {
326
        "name": instance["name"],
327
        "os": qa_config.get("os"),
328
        "disk_template": constants.DT_PLAIN,
329
        "pnode": node["primary"],
330
        "memory": memory,
331
        "disks": disk_sizes,
332
        }
333

    
334
      (job_id, ) = _DoTests([
335
        ("/2/instances", _VerifyReturnsJob, "POST", body),
336
        ])
337

    
338
    _WaitForRapiJob(job_id)
339

    
340
    return instance
341
  except:
342
    qa_config.ReleaseInstance(instance)
343
    raise
344

    
345

    
346
def TestRapiInstanceRemove(instance, use_client):
347
  """Test removing instance via RAPI"""
348
  if use_client:
349
    job_id = _rapi_client.DeleteInstance(instance["name"])
350
  else:
351
    (job_id, ) = _DoTests([
352
      ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
353
      ])
354

    
355
  _WaitForRapiJob(job_id)
356

    
357
  qa_config.ReleaseInstance(instance)
358

    
359

    
360
def TestRapiInstanceMigrate(instance):
361
  """Test migrating instance via RAPI"""
362
  # Move to secondary node
363
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
364
  # And back to previous primary
365
  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
366

    
367

    
368
def TestRapiInstanceRename(instance, rename_target):
369
  """Test renaming instance via RAPI"""
370
  rename_source = instance["name"]
371

    
372
  for name1, name2 in [(rename_source, rename_target),
373
                       (rename_target, rename_source)]:
374
    _WaitForRapiJob(_rapi_client.RenameInstance(name1, name2))
375

    
376

    
377
def TestRapiInstanceModify(instance):
378
  """Test modifying instance via RAPI"""
379
  def _ModifyInstance(**kwargs):
380
    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
381

    
382
  _ModifyInstance(hvparams={
383
    constants.HV_KERNEL_ARGS: "single",
384
    })
385

    
386
  _ModifyInstance(beparams={
387
    constants.BE_VCPUS: 3,
388
    })
389

    
390
  _ModifyInstance(beparams={
391
    constants.BE_VCPUS: constants.VALUE_DEFAULT,
392
    })
393

    
394
  _ModifyInstance(hvparams={
395
    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
396
    })
397

    
398

    
399
def TestInterClusterInstanceMove(src_instance, dest_instance, pnode, snode):
400
  """Test tools/move-instance"""
401
  master = qa_config.GetMasterNode()
402

    
403
  rapi_pw_file = tempfile.NamedTemporaryFile()
404
  rapi_pw_file.write(_rapi_password)
405
  rapi_pw_file.flush()
406

    
407
  # TODO: Run some instance tests before moving back
408
  for srcname, destname in [(src_instance["name"], dest_instance["name"]),
409
                            (dest_instance["name"], src_instance["name"])]:
410
    cmd = [
411
      "../tools/move-instance",
412
      "--verbose",
413
      "--src-ca-file=%s" % _rapi_ca.name,
414
      "--src-username=%s" % _rapi_username,
415
      "--src-password-file=%s" % rapi_pw_file.name,
416
      "--dest-instance-name=%s" % destname,
417
      "--dest-primary-node=%s" % pnode["primary"],
418
      "--dest-secondary-node=%s" % snode["primary"],
419

    
420
      master["primary"],
421
      master["primary"],
422
      srcname,
423
      ]
424

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