Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ b780c231

History | View | Annotate | Download (16.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2011, 2012, 2013 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
"""Node-related QA tests.
23

24
"""
25

    
26
from ganeti import utils
27
from ganeti import constants
28
from ganeti import query
29
from ganeti import serializer
30

    
31
import qa_config
32
import qa_error
33
import qa_utils
34

    
35
from qa_utils import AssertCommand, AssertEqual
36

    
37

    
38
def _NodeAdd(node, readd=False):
39
  if not readd and node.added:
40
    raise qa_error.Error("Node %s already in cluster" % node.primary)
41
  elif readd and not node.added:
42
    raise qa_error.Error("Node %s not yet in cluster" % node.primary)
43

    
44
  cmd = ["gnt-node", "add", "--no-ssh-key-check"]
45
  if node.secondary:
46
    cmd.append("--secondary-ip=%s" % node.secondary)
47
  if readd:
48
    cmd.append("--readd")
49
  cmd.append(node.primary)
50

    
51
  AssertCommand(cmd)
52

    
53
  if readd:
54
    assert node.added
55
  else:
56
    node.MarkAdded()
57

    
58

    
59
def _NodeRemove(node):
60
  AssertCommand(["gnt-node", "remove", node.primary])
61
  node.MarkRemoved()
62

    
63

    
64
def MakeNodeOffline(node, value):
65
  """gnt-node modify --offline=value"""
66
  # value in ["yes", "no"]
67
  AssertCommand(["gnt-node", "modify", "--offline", value, node.primary])
68

    
69

    
70
def TestNodeAddAll():
71
  """Adding all nodes to cluster."""
72
  master = qa_config.GetMasterNode()
73
  for node in qa_config.get("nodes"):
74
    if node != master:
75
      _NodeAdd(node, readd=False)
76

    
77

    
78
def MarkNodeAddedAll():
79
  """Mark all nodes as added.
80

81
  This is useful if we don't create the cluster ourselves (in qa).
82

83
  """
84
  master = qa_config.GetMasterNode()
85
  for node in qa_config.get("nodes"):
86
    if node != master:
87
      node.MarkAdded()
88

    
89

    
90
def TestNodeRemoveAll():
91
  """Removing all nodes from cluster."""
92
  master = qa_config.GetMasterNode()
93
  for node in qa_config.get("nodes"):
94
    if node != master:
95
      _NodeRemove(node)
96

    
97

    
98
def TestNodeReadd(node):
99
  """gnt-node add --readd"""
100
  _NodeAdd(node, readd=True)
101

    
102

    
103
def TestNodeInfo():
104
  """gnt-node info"""
105
  AssertCommand(["gnt-node", "info"])
106

    
107

    
108
def TestNodeVolumes():
109
  """gnt-node volumes"""
110
  AssertCommand(["gnt-node", "volumes"])
111

    
112

    
113
def TestNodeStorage():
114
  """gnt-node storage"""
115
  master = qa_config.GetMasterNode()
116
  enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
117

    
118
  # FIXME: test all storage_types in constants.VALID_STORAGE_TYPES
119
  # as soon as they are implemented.
120
  for storage_type in [constants.ST_FILE, constants.ST_LVM_VG,
121
                       constants.ST_LVM_PV]:
122

    
123
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type]
124

    
125
    # Skip file storage if not enabled, otherwise QA will fail; we
126
    # just test for basic failure, but otherwise skip the rest of the
127
    # tests
128
    if storage_type == constants.ST_FILE and not \
129
        ((constants.DT_FILE in enabled_disk_templates) or
130
         (constants.DT_SHARED_FILE in enabled_disk_templates)):
131
      AssertCommand(cmd, fail=True)
132
      continue
133

    
134
    # Test simple list
135
    AssertCommand(cmd)
136

    
137
    # Test all storage fields
138
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
139
           "--output=%s" % ",".join(list(constants.VALID_STORAGE_FIELDS) +
140
                                    [constants.SF_NODE, constants.SF_TYPE])]
141
    AssertCommand(cmd)
142

    
143
    # Get list of valid storage devices
144
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
145
           "--output=node,name,allocatable", "--separator=|",
146
           "--no-headers"]
147
    output = qa_utils.GetCommandOutput(master.primary,
148
                                       utils.ShellQuoteArgs(cmd))
149

    
150
    # Test with up to two devices
151
    testdevcount = 2
152

    
153
    for line in output.splitlines()[:testdevcount]:
154
      (node_name, st_name, st_allocatable) = line.split("|")
155

    
156
      # Dummy modification without any changes
157
      cmd = ["gnt-node", "modify-storage", node_name, storage_type, st_name]
158
      AssertCommand(cmd)
159

    
160
      # Make sure we end up with the same value as before
161
      if st_allocatable.lower() == "y":
162
        test_allocatable = ["no", "yes"]
163
      else:
164
        test_allocatable = ["yes", "no"]
165

    
166
      fail = (constants.SF_ALLOCATABLE not in
167
              constants.MODIFIABLE_STORAGE_FIELDS.get(storage_type, []))
168

    
169
      for i in test_allocatable:
170
        AssertCommand(["gnt-node", "modify-storage", "--allocatable", i,
171
                       node_name, storage_type, st_name], fail=fail)
172

    
173
        # Verify list output
174
        cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
175
               "--output=name,allocatable", "--separator=|",
176
               "--no-headers", node_name]
177
        listout = qa_utils.GetCommandOutput(master.primary,
178
                                            utils.ShellQuoteArgs(cmd))
179
        for line in listout.splitlines():
180
          (vfy_name, vfy_allocatable) = line.split("|")
181
          if vfy_name == st_name and not fail:
182
            AssertEqual(vfy_allocatable, i[0].upper())
183
          else:
184
            AssertEqual(vfy_allocatable, st_allocatable)
185

    
186
      # Test repair functionality
187
      fail = (constants.SO_FIX_CONSISTENCY not in
188
              constants.VALID_STORAGE_OPERATIONS.get(storage_type, []))
189
      AssertCommand(["gnt-node", "repair-storage", node_name,
190
                     storage_type, st_name], fail=fail)
191

    
192

    
193
def TestNodeFailover(node, node2):
194
  """gnt-node failover"""
195
  if qa_utils.GetNodeInstances(node2, secondaries=False):
196
    raise qa_error.UnusableNodeError("Secondary node has at least one"
197
                                     " primary instance. This test requires"
198
                                     " it to have no primary instances.")
199

    
200
  # Fail over to secondary node
201
  AssertCommand(["gnt-node", "failover", "-f", node.primary])
202

    
203
  # ... and back again.
204
  AssertCommand(["gnt-node", "failover", "-f", node2.primary])
205

    
206

    
207
def TestNodeMigrate(node, node2):
208
  """gnt-node migrate"""
209
  if qa_utils.GetNodeInstances(node2, secondaries=False):
210
    raise qa_error.UnusableNodeError("Secondary node has at least one"
211
                                     " primary instance. This test requires"
212
                                     " it to have no primary instances.")
213

    
214
  # Migrate to secondary node
215
  AssertCommand(["gnt-node", "migrate", "-f", node.primary])
216

    
217
  # ... and back again.
218
  AssertCommand(["gnt-node", "migrate", "-f", node2.primary])
219

    
220

    
221
def TestNodeEvacuate(node, node2):
222
  """gnt-node evacuate"""
223
  node3 = qa_config.AcquireNode(exclude=[node, node2])
224
  try:
225
    if qa_utils.GetNodeInstances(node3, secondaries=True):
226
      raise qa_error.UnusableNodeError("Evacuation node has at least one"
227
                                       " secondary instance. This test requires"
228
                                       " it to have no secondary instances.")
229

    
230
    # Evacuate all secondary instances
231
    AssertCommand(["gnt-node", "evacuate", "-f",
232
                   "--new-secondary=%s" % node3.primary, node2.primary])
233

    
234
    # ... and back again.
235
    AssertCommand(["gnt-node", "evacuate", "-f",
236
                   "--new-secondary=%s" % node2.primary, node3.primary])
237
  finally:
238
    node3.Release()
239

    
240

    
241
def TestNodeModify(node):
242
  """gnt-node modify"""
243

    
244
  # make sure enough master candidates will be available by disabling the
245
  # master candidate role first with --auto-promote
246
  AssertCommand(["gnt-node", "modify", "--master-candidate=no",
247
                "--auto-promote", node.primary])
248

    
249
  # now it's save to force-remove the master candidate role
250
  for flag in ["master-candidate", "drained", "offline"]:
251
    for value in ["yes", "no"]:
252
      AssertCommand(["gnt-node", "modify", "--force",
253
                     "--%s=%s" % (flag, value), node.primary])
254

    
255
  AssertCommand(["gnt-node", "modify", "--master-candidate=yes", node.primary])
256

    
257
  # Test setting secondary IP address
258
  AssertCommand(["gnt-node", "modify", "--secondary-ip=%s" % node.secondary,
259
                 node.primary])
260

    
261

    
262
def _CreateOobScriptStructure():
263
  """Create a simple OOB handling script and its structure."""
264
  master = qa_config.GetMasterNode()
265

    
266
  data_path = qa_utils.UploadData(master.primary, "")
267
  verify_path = qa_utils.UploadData(master.primary, "")
268
  exit_code_path = qa_utils.UploadData(master.primary, "")
269

    
270
  oob_script = (("#!/bin/bash\n"
271
                 "echo \"$@\" > %s\n"
272
                 "cat %s\n"
273
                 "exit $(< %s)\n") %
274
                (utils.ShellQuote(verify_path), utils.ShellQuote(data_path),
275
                 utils.ShellQuote(exit_code_path)))
276
  oob_path = qa_utils.UploadData(master.primary, oob_script, mode=0700)
277

    
278
  return [oob_path, verify_path, data_path, exit_code_path]
279

    
280

    
281
def _UpdateOobFile(path, data):
282
  """Updates the data file with data."""
283
  master = qa_config.GetMasterNode()
284
  qa_utils.UploadData(master.primary, data, filename=path)
285

    
286

    
287
def _AssertOobCall(verify_path, expected_args):
288
  """Assert the OOB call was performed with expetected args."""
289
  master = qa_config.GetMasterNode()
290

    
291
  verify_output_cmd = utils.ShellQuoteArgs(["cat", verify_path])
292
  output = qa_utils.GetCommandOutput(master.primary, verify_output_cmd,
293
                                     tty=False)
294

    
295
  AssertEqual(expected_args, output.strip())
296

    
297

    
298
def TestOutOfBand():
299
  """gnt-node power"""
300
  master = qa_config.GetMasterNode()
301

    
302
  node = qa_config.AcquireNode(exclude=master)
303

    
304
  master_name = master.primary
305
  node_name = node.primary
306
  full_node_name = qa_utils.ResolveNodeName(node)
307

    
308
  (oob_path, verify_path,
309
   data_path, exit_code_path) = _CreateOobScriptStructure()
310

    
311
  try:
312
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
313
                   "oob_program=%s" % oob_path])
314

    
315
    # No data, exit 0
316
    _UpdateOobFile(exit_code_path, "0")
317

    
318
    AssertCommand(["gnt-node", "power", "on", node_name])
319
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
320

    
321
    AssertCommand(["gnt-node", "power", "-f", "off", node_name])
322
    _AssertOobCall(verify_path, "power-off %s" % full_node_name)
323

    
324
    # Power off on master without options should fail
325
    AssertCommand(["gnt-node", "power", "-f", "off", master_name], fail=True)
326
    # With force master it should still fail
327
    AssertCommand(["gnt-node", "power", "-f", "--ignore-status", "off",
328
                   master_name],
329
                  fail=True)
330

    
331
    # Verify we can't transform back to online when not yet powered on
332
    AssertCommand(["gnt-node", "modify", "-O", "no", node_name],
333
                  fail=True)
334
    # Now reset state
335
    AssertCommand(["gnt-node", "modify", "-O", "no", "--node-powered", "yes",
336
                   node_name])
337

    
338
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name])
339
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
340

    
341
    # Those commands should fail as they expect output which isn't provided yet
342
    # But they should have called the oob helper nevermind
343
    AssertCommand(["gnt-node", "power", "status", node_name],
344
                  fail=True)
345
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
346

    
347
    AssertCommand(["gnt-node", "health", node_name],
348
                  fail=True)
349
    _AssertOobCall(verify_path, "health %s" % full_node_name)
350

    
351
    AssertCommand(["gnt-node", "health"], fail=True)
352

    
353
    # Correct Data, exit 0
354
    _UpdateOobFile(data_path, serializer.DumpJson({"powered": True}))
355

    
356
    AssertCommand(["gnt-node", "power", "status", node_name])
357
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
358

    
359
    _UpdateOobFile(data_path, serializer.DumpJson([["temp", "OK"],
360
                                                   ["disk0", "CRITICAL"]]))
361

    
362
    AssertCommand(["gnt-node", "health", node_name])
363
    _AssertOobCall(verify_path, "health %s" % full_node_name)
364

    
365
    AssertCommand(["gnt-node", "health"])
366

    
367
    # Those commands should fail as they expect no data regardless of exit 0
368
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
369
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
370

    
371
    try:
372
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
373
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
374
    finally:
375
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
376

    
377
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
378
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
379

    
380
    # Data, exit 1 (all should fail)
381
    _UpdateOobFile(exit_code_path, "1")
382

    
383
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
384
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
385

    
386
    try:
387
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
388
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
389
    finally:
390
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
391

    
392
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
393
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
394

    
395
    AssertCommand(["gnt-node", "power", "status", node_name],
396
                  fail=True)
397
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
398

    
399
    AssertCommand(["gnt-node", "health", node_name],
400
                  fail=True)
401
    _AssertOobCall(verify_path, "health %s" % full_node_name)
402

    
403
    AssertCommand(["gnt-node", "health"], fail=True)
404

    
405
    # No data, exit 1 (all should fail)
406
    _UpdateOobFile(data_path, "")
407
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
408
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
409

    
410
    try:
411
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
412
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
413
    finally:
414
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
415

    
416
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
417
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
418

    
419
    AssertCommand(["gnt-node", "power", "status", node_name],
420
                  fail=True)
421
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
422

    
423
    AssertCommand(["gnt-node", "health", node_name],
424
                  fail=True)
425
    _AssertOobCall(verify_path, "health %s" % full_node_name)
426

    
427
    AssertCommand(["gnt-node", "health"], fail=True)
428

    
429
    # Different OOB script for node
430
    verify_path2 = qa_utils.UploadData(master.primary, "")
431
    oob_script = ("#!/bin/sh\n"
432
                  "echo \"$@\" > %s\n") % verify_path2
433
    oob_path2 = qa_utils.UploadData(master.primary, oob_script, mode=0700)
434

    
435
    try:
436
      AssertCommand(["gnt-node", "modify", "--node-parameters",
437
                     "oob_program=%s" % oob_path2, node_name])
438
      AssertCommand(["gnt-node", "power", "on", node_name])
439
      _AssertOobCall(verify_path2, "power-on %s" % full_node_name)
440
    finally:
441
      AssertCommand(["gnt-node", "modify", "--node-parameters",
442
                     "oob_program=default", node_name])
443
      AssertCommand(["rm", "-f", oob_path2, verify_path2])
444
  finally:
445
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
446
                   "oob_program="])
447
    AssertCommand(["rm", "-f", oob_path, verify_path, data_path,
448
                   exit_code_path])
449

    
450

    
451
def TestNodeList():
452
  """gnt-node list"""
453
  qa_utils.GenericQueryTest("gnt-node", query.NODE_FIELDS.keys())
454

    
455

    
456
def TestNodeListFields():
457
  """gnt-node list-fields"""
458
  qa_utils.GenericQueryFieldsTest("gnt-node", query.NODE_FIELDS.keys())
459

    
460

    
461
def TestNodeListDrbd(node):
462
  """gnt-node list-drbd"""
463
  AssertCommand(["gnt-node", "list-drbd", node.primary])
464

    
465

    
466
def _BuildSetESCmd(action, value, node_name):
467
  cmd = ["gnt-node"]
468
  if action == "add":
469
    cmd.extend(["add", "--readd"])
470
  else:
471
    cmd.append("modify")
472
  cmd.extend(["--node-parameters", "exclusive_storage=%s" % value, node_name])
473
  return cmd
474

    
475

    
476
def TestExclStorSingleNode(node):
477
  """gnt-node add/modify cannot change the exclusive_storage flag.
478

479
  """
480
  for action in ["add", "modify"]:
481
    for value in (True, False, "default"):
482
      AssertCommand(_BuildSetESCmd(action, value, node.primary), fail=True)