Statistics
| Branch: | Tag: | Revision:

root / qa / qa_cluster.py @ 13d2e231

History | View | Annotate | Download (16.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Cluster related QA tests.
23

24
"""
25

    
26
import tempfile
27
import os.path
28

    
29
from ganeti import constants
30
from ganeti import compat
31
from ganeti import utils
32

    
33
import qa_config
34
import qa_utils
35
import qa_error
36

    
37
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
38

    
39

    
40
#: cluster verify command
41
_CLUSTER_VERIFY = ["gnt-cluster", "verify"]
42

    
43

    
44
def _RemoveFileFromAllNodes(filename):
45
  """Removes a file from all nodes.
46

47
  """
48
  for node in qa_config.get("nodes"):
49
    AssertCommand(["rm", "-f", filename], node=node)
50

    
51

    
52
def _CheckFileOnAllNodes(filename, content):
53
  """Verifies the content of the given file on all nodes.
54

55
  """
56
  cmd = utils.ShellQuoteArgs(["cat", filename])
57
  for node in qa_config.get("nodes"):
58
    AssertEqual(qa_utils.GetCommandOutput(node["primary"], cmd), content)
59

    
60

    
61
def TestClusterInit(rapi_user, rapi_secret):
62
  """gnt-cluster init"""
63
  # data for testing failures due to bad keys/values for disk parameters
64
  fail_params = ("-D nonexistent:resync-rate=1",
65
                 "-D drbd:nonexistent=1",
66
                 "-D drbd:resync-rate=invalid")
67

    
68
  master = qa_config.GetMasterNode()
69

    
70
  rapi_dir = os.path.dirname(constants.RAPI_USERS_FILE)
71

    
72
  # First create the RAPI credentials
73
  fh = tempfile.NamedTemporaryFile()
74
  try:
75
    fh.write("%s %s write\n" % (rapi_user, rapi_secret))
76
    fh.flush()
77

    
78
    tmpru = qa_utils.UploadFile(master["primary"], fh.name)
79
    try:
80
      AssertCommand(["mkdir", "-p", rapi_dir])
81
      AssertCommand(["mv", tmpru, constants.RAPI_USERS_FILE])
82
    finally:
83
      AssertCommand(["rm", "-f", tmpru])
84
  finally:
85
    fh.close()
86

    
87
  # Initialize cluster
88
  cmd = ["gnt-cluster", "init"]
89

    
90
  cmd.append("--primary-ip-version=%d" %
91
             qa_config.get("primary_ip_version", 4))
92

    
93
  if master.get("secondary", None):
94
    cmd.append("--secondary-ip=%s" % master["secondary"])
95

    
96
  bridge = qa_config.get("bridge", None)
97
  if bridge:
98
    cmd.append("--bridge=%s" % bridge)
99
    cmd.append("--master-netdev=%s" % bridge)
100

    
101
  htype = qa_config.get("enabled-hypervisors", None)
102
  if htype:
103
    cmd.append("--enabled-hypervisors=%s" % htype)
104

    
105
  # test gnt-cluster init failures due to bad keys/values in disk parameters
106
  for param in fail_params:
107
    cmd.extend([param, qa_config.get("name")])
108
    AssertCommand(cmd, fail=True)
109
    cmd.pop()
110
    cmd.pop()
111

    
112
  cmd.append(qa_config.get("name"))
113
  AssertCommand(cmd)
114

    
115
  cmd = ["gnt-cluster", "modify"]
116
  # test gnt-cluster modify failures due to bad keys/values in disk parameters
117
  for param in fail_params:
118
    cmd.append(param)
119
    AssertCommand(cmd, fail=True)
120
    cmd.pop()
121

    
122
  # hypervisor parameter modifications
123
  hvp = qa_config.get("hypervisor-parameters", {})
124
  for k, v in hvp.items():
125
    cmd.extend(["-H", "%s:%s" % (k, v)])
126
  # backend parameter modifications
127
  bep = qa_config.get("backend-parameters", "")
128
  if bep:
129
    cmd.extend(["-B", bep])
130

    
131
  if len(cmd) > 2:
132
    AssertCommand(cmd)
133

    
134
  # OS parameters
135
  osp = qa_config.get("os-parameters", {})
136
  for k, v in osp.items():
137
    AssertCommand(["gnt-os", "modify", "-O", v, k])
138

    
139
  # OS hypervisor parameters
140
  os_hvp = qa_config.get("os-hvp", {})
141
  for os_name in os_hvp:
142
    for hv, hvp in os_hvp[os_name].items():
143
      AssertCommand(["gnt-os", "modify", "-H", "%s:%s" % (hv, hvp), os_name])
144

    
145

    
146
def TestClusterRename():
147
  """gnt-cluster rename"""
148
  cmd = ["gnt-cluster", "rename", "-f"]
149

    
150
  original_name = qa_config.get("name")
151
  rename_target = qa_config.get("rename", None)
152
  if rename_target is None:
153
    print qa_utils.FormatError('"rename" entry is missing')
154
    return
155

    
156
  for data in [
157
    cmd + [rename_target],
158
    _CLUSTER_VERIFY,
159
    cmd + [original_name],
160
    _CLUSTER_VERIFY,
161
    ]:
162
    AssertCommand(data)
163

    
164

    
165
def TestClusterOob():
166
  """out-of-band framework"""
167
  oob_path_exists = "/tmp/ganeti-qa-oob-does-exist-%s" % utils.NewUUID()
168

    
169
  AssertCommand(_CLUSTER_VERIFY)
170
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
171
                 "oob_program=/tmp/ganeti-qa-oob-does-not-exist-%s" %
172
                 utils.NewUUID()])
173

    
174
  AssertCommand(_CLUSTER_VERIFY, fail=True)
175

    
176
  AssertCommand(["touch", oob_path_exists])
177
  AssertCommand(["chmod", "0400", oob_path_exists])
178
  AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
179

    
180
  try:
181
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
182
                   "oob_program=%s" % oob_path_exists])
183

    
184
    AssertCommand(_CLUSTER_VERIFY, fail=True)
185

    
186
    AssertCommand(["chmod", "0500", oob_path_exists])
187
    AssertCommand(["gnt-cluster", "copyfile", oob_path_exists])
188

    
189
    AssertCommand(_CLUSTER_VERIFY)
190
  finally:
191
    AssertCommand(["gnt-cluster", "command", "rm", oob_path_exists])
192

    
193
  AssertCommand(["gnt-cluster", "modify", "--node-parameters",
194
                 "oob_program="])
195

    
196

    
197
def TestClusterEpo():
198
  """gnt-cluster epo"""
199
  master = qa_config.GetMasterNode()
200

    
201
  # Assert that OOB is unavailable for all nodes
202
  result_output = GetCommandOutput(master["primary"],
203
                                   "gnt-node list --verbose --no-header -o"
204
                                   " powered")
205
  AssertEqual(compat.all(powered == "(unavail)"
206
                         for powered in result_output.splitlines()), True)
207

    
208
  # Conflicting
209
  AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
210
  # --all doesn't expect arguments
211
  AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
212

    
213
  # Unless --all is given master is not allowed to be in the list
214
  AssertCommand(["gnt-cluster", "epo", "-f", master["primary"]], fail=True)
215

    
216
  # This shouldn't fail
217
  AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
218

    
219
  # All instances should have been stopped now
220
  result_output = GetCommandOutput(master["primary"],
221
                                   "gnt-instance list --no-header -o status")
222
  AssertEqual(compat.all(status == "ADMIN_down"
223
                         for status in result_output.splitlines()), True)
224

    
225
  # Now start everything again
226
  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
227

    
228
  # All instances should have been started now
229
  result_output = GetCommandOutput(master["primary"],
230
                                   "gnt-instance list --no-header -o status")
231
  AssertEqual(compat.all(status == "running"
232
                         for status in result_output.splitlines()), True)
233

    
234

    
235
def TestClusterVerify():
236
  """gnt-cluster verify"""
237
  AssertCommand(_CLUSTER_VERIFY)
238
  AssertCommand(["gnt-cluster", "verify-disks"])
239

    
240

    
241
def TestJobqueue():
242
  """gnt-debug test-jobqueue"""
243
  AssertCommand(["gnt-debug", "test-jobqueue"])
244

    
245

    
246
def TestDelay(node):
247
  """gnt-debug delay"""
248
  AssertCommand(["gnt-debug", "delay", "1"])
249
  AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
250
  AssertCommand(["gnt-debug", "delay", "--no-master",
251
                 "-n", node["primary"], "1"])
252

    
253

    
254
def TestClusterReservedLvs():
255
  """gnt-cluster reserved lvs"""
256
  for fail, cmd in [
257
    (False, _CLUSTER_VERIFY),
258
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
259
    (False, ["lvcreate", "-L1G", "-nqa-test", "xenvg"]),
260
    (True, _CLUSTER_VERIFY),
261
    (False, ["gnt-cluster", "modify", "--reserved-lvs",
262
             "xenvg/qa-test,.*/other-test"]),
263
    (False, _CLUSTER_VERIFY),
264
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ".*/qa-.*"]),
265
    (False, _CLUSTER_VERIFY),
266
    (False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
267
    (True, _CLUSTER_VERIFY),
268
    (False, ["lvremove", "-f", "xenvg/qa-test"]),
269
    (False, _CLUSTER_VERIFY),
270
    ]:
271
    AssertCommand(cmd, fail=fail)
272

    
273

    
274
def TestClusterModifyBe():
275
  """gnt-cluster modify -B"""
276
  for fail, cmd in [
277
    # max/min mem
278
    (False, ["gnt-cluster", "modify", "-B", "maxmem=256"]),
279
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
280
    (False, ["gnt-cluster", "modify", "-B", "minmem=256"]),
281
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
282
    (True, ["gnt-cluster", "modify", "-B", "maxmem=a"]),
283
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 256$'"]),
284
    (True, ["gnt-cluster", "modify", "-B", "minmem=a"]),
285
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 256$'"]),
286
    (False, ["gnt-cluster", "modify", "-B", "maxmem=128,minmem=128"]),
287
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *maxmem: 128$'"]),
288
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *minmem: 128$'"]),
289
    # vcpus
290
    (False, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
291
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
292
    (True, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
293
    (False, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
294
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
295
    # auto_balance
296
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
297
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
298
    (True, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
299
    (False, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
300
    (False, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
301
    ]:
302
    AssertCommand(cmd, fail=fail)
303

    
304
  # redo the original-requested BE parameters, if any
305
  bep = qa_config.get("backend-parameters", "")
306
  if bep:
307
    AssertCommand(["gnt-cluster", "modify", "-B", bep])
308

    
309

    
310
def TestClusterInfo():
311
  """gnt-cluster info"""
312
  AssertCommand(["gnt-cluster", "info"])
313

    
314

    
315
def TestClusterRedistConf():
316
  """gnt-cluster redist-conf"""
317
  AssertCommand(["gnt-cluster", "redist-conf"])
318

    
319

    
320
def TestClusterGetmaster():
321
  """gnt-cluster getmaster"""
322
  AssertCommand(["gnt-cluster", "getmaster"])
323

    
324

    
325
def TestClusterVersion():
326
  """gnt-cluster version"""
327
  AssertCommand(["gnt-cluster", "version"])
328

    
329

    
330
def TestClusterRenewCrypto():
331
  """gnt-cluster renew-crypto"""
332
  master = qa_config.GetMasterNode()
333

    
334
  # Conflicting options
335
  cmd = ["gnt-cluster", "renew-crypto", "--force",
336
         "--new-cluster-certificate", "--new-confd-hmac-key"]
337
  conflicting = [
338
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
339
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
340
    ]
341
  for i in conflicting:
342
    AssertCommand(cmd + i, fail=True)
343

    
344
  # Invalid RAPI certificate
345
  cmd = ["gnt-cluster", "renew-crypto", "--force",
346
         "--rapi-certificate=/dev/null"]
347
  AssertCommand(cmd, fail=True)
348

    
349
  rapi_cert_backup = qa_utils.BackupFile(master["primary"],
350
                                         constants.RAPI_CERT_FILE)
351
  try:
352
    # Custom RAPI certificate
353
    fh = tempfile.NamedTemporaryFile()
354

    
355
    # Ensure certificate doesn't cause "gnt-cluster verify" to complain
356
    validity = constants.SSL_CERT_EXPIRATION_WARN * 3
357

    
358
    utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
359

    
360
    tmpcert = qa_utils.UploadFile(master["primary"], fh.name)
361
    try:
362
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
363
                     "--rapi-certificate=%s" % tmpcert])
364
    finally:
365
      AssertCommand(["rm", "-f", tmpcert])
366

    
367
    # Custom cluster domain secret
368
    cds_fh = tempfile.NamedTemporaryFile()
369
    cds_fh.write(utils.GenerateSecret())
370
    cds_fh.write("\n")
371
    cds_fh.flush()
372

    
373
    tmpcds = qa_utils.UploadFile(master["primary"], cds_fh.name)
374
    try:
375
      AssertCommand(["gnt-cluster", "renew-crypto", "--force",
376
                     "--cluster-domain-secret=%s" % tmpcds])
377
    finally:
378
      AssertCommand(["rm", "-f", tmpcds])
379

    
380
    # Normal case
381
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
382
                   "--new-cluster-certificate", "--new-confd-hmac-key",
383
                   "--new-rapi-certificate", "--new-cluster-domain-secret"])
384

    
385
    # Restore RAPI certificate
386
    AssertCommand(["gnt-cluster", "renew-crypto", "--force",
387
                   "--rapi-certificate=%s" % rapi_cert_backup])
388
  finally:
389
    AssertCommand(["rm", "-f", rapi_cert_backup])
390

    
391

    
392
def TestClusterBurnin():
393
  """Burnin"""
394
  master = qa_config.GetMasterNode()
395

    
396
  options = qa_config.get("options", {})
397
  disk_template = options.get("burnin-disk-template", "drbd")
398
  parallel = options.get("burnin-in-parallel", False)
399
  check_inst = options.get("burnin-check-instances", False)
400
  do_rename = options.get("burnin-rename", "")
401
  do_reboot = options.get("burnin-reboot", True)
402
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
403

    
404
  # Get as many instances as we need
405
  instances = []
406
  try:
407
    try:
408
      num = qa_config.get("options", {}).get("burnin-instances", 1)
409
      for _ in range(0, num):
410
        instances.append(qa_config.AcquireInstance())
411
    except qa_error.OutOfInstancesError:
412
      print "Not enough instances, continuing anyway."
413

    
414
    if len(instances) < 1:
415
      raise qa_error.Error("Burnin needs at least one instance")
416

    
417
    script = qa_utils.UploadFile(master["primary"], "../tools/burnin")
418
    try:
419
      # Run burnin
420
      cmd = [script,
421
             "--os=%s" % qa_config.get("os"),
422
             "--disk-size=%s" % ",".join(qa_config.get("disk")),
423
             "--disk-growth=%s" % ",".join(qa_config.get("disk-growth")),
424
             "--disk-template=%s" % disk_template]
425
      if parallel:
426
        cmd.append("--parallel")
427
        cmd.append("--early-release")
428
      if check_inst:
429
        cmd.append("--http-check")
430
      if do_rename:
431
        cmd.append("--rename=%s" % do_rename)
432
      if not do_reboot:
433
        cmd.append("--no-reboot")
434
      else:
435
        cmd.append("--reboot-types=%s" % ",".join(reboot_types))
436
      cmd += [inst["name"] for inst in instances]
437
      AssertCommand(cmd)
438
    finally:
439
      AssertCommand(["rm", "-f", script])
440

    
441
  finally:
442
    for inst in instances:
443
      qa_config.ReleaseInstance(inst)
444

    
445

    
446
def TestClusterMasterFailover():
447
  """gnt-cluster master-failover"""
448
  master = qa_config.GetMasterNode()
449
  failovermaster = qa_config.AcquireNode(exclude=master)
450

    
451
  cmd = ["gnt-cluster", "master-failover"]
452
  try:
453
    AssertCommand(cmd, node=failovermaster)
454
    # Back to original master node
455
    AssertCommand(cmd, node=master)
456
  finally:
457
    qa_config.ReleaseNode(failovermaster)
458

    
459

    
460
def TestClusterMasterFailoverWithDrainedQueue():
461
  """gnt-cluster master-failover with drained queue"""
462
  drain_check = ["test", "-f", constants.JOB_QUEUE_DRAIN_FILE]
463

    
464
  master = qa_config.GetMasterNode()
465
  failovermaster = qa_config.AcquireNode(exclude=master)
466

    
467
  # Ensure queue is not drained
468
  for node in [master, failovermaster]:
469
    AssertCommand(drain_check, node=node, fail=True)
470

    
471
  # Drain queue on failover master
472
  AssertCommand(["touch", constants.JOB_QUEUE_DRAIN_FILE], node=failovermaster)
473

    
474
  cmd = ["gnt-cluster", "master-failover"]
475
  try:
476
    AssertCommand(drain_check, node=failovermaster)
477
    AssertCommand(cmd, node=failovermaster)
478
    AssertCommand(drain_check, fail=True)
479
    AssertCommand(drain_check, node=failovermaster, fail=True)
480

    
481
    # Back to original master node
482
    AssertCommand(cmd, node=master)
483
  finally:
484
    qa_config.ReleaseNode(failovermaster)
485

    
486
  AssertCommand(drain_check, fail=True)
487
  AssertCommand(drain_check, node=failovermaster, fail=True)
488

    
489

    
490
def TestClusterCopyfile():
491
  """gnt-cluster copyfile"""
492
  master = qa_config.GetMasterNode()
493

    
494
  uniqueid = utils.NewUUID()
495

    
496
  # Create temporary file
497
  f = tempfile.NamedTemporaryFile()
498
  f.write(uniqueid)
499
  f.flush()
500
  f.seek(0)
501

    
502
  # Upload file to master node
503
  testname = qa_utils.UploadFile(master["primary"], f.name)
504
  try:
505
    # Copy file to all nodes
506
    AssertCommand(["gnt-cluster", "copyfile", testname])
507
    _CheckFileOnAllNodes(testname, uniqueid)
508
  finally:
509
    _RemoveFileFromAllNodes(testname)
510

    
511

    
512
def TestClusterCommand():
513
  """gnt-cluster command"""
514
  uniqueid = utils.NewUUID()
515
  rfile = "/tmp/gnt%s" % utils.NewUUID()
516
  rcmd = utils.ShellQuoteArgs(["echo", "-n", uniqueid])
517
  cmd = utils.ShellQuoteArgs(["gnt-cluster", "command",
518
                              "%s >%s" % (rcmd, rfile)])
519

    
520
  try:
521
    AssertCommand(cmd)
522
    _CheckFileOnAllNodes(rfile, uniqueid)
523
  finally:
524
    _RemoveFileFromAllNodes(rfile)
525

    
526

    
527
def TestClusterDestroy():
528
  """gnt-cluster destroy"""
529
  AssertCommand(["gnt-cluster", "destroy", "--yes-do-it"])
530

    
531

    
532
def TestClusterRepairDiskSizes():
533
  """gnt-cluster repair-disk-sizes"""
534
  AssertCommand(["gnt-cluster", "repair-disk-sizes"])