Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ 52e9bef0

History | View | Annotate | Download (15.7 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

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

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

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

    
131
    # Test simple list
132
    AssertCommand(cmd)
133

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

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

    
147
    # Test with up to two devices
148
    testdevcount = 2
149

    
150
    for line in output.splitlines()[:testdevcount]:
151
      (node_name, st_name, st_allocatable) = line.split("|")
152

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

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

    
163
      fail = (constants.SF_ALLOCATABLE not in
164
              constants.MODIFIABLE_STORAGE_FIELDS.get(storage_type, []))
165

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

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

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

    
189

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

    
197
  # Fail over to secondary node
198
  AssertCommand(["gnt-node", "failover", "-f", node.primary])
199

    
200
  # ... and back again.
201
  AssertCommand(["gnt-node", "failover", "-f", node2.primary])
202

    
203

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

    
211
  # Migrate to secondary node
212
  AssertCommand(["gnt-node", "migrate", "-f", node.primary])
213

    
214
  # ... and back again.
215
  AssertCommand(["gnt-node", "migrate", "-f", node2.primary])
216

    
217

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

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

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

    
237

    
238
def TestNodeModify(node):
239
  """gnt-node modify"""
240
  for flag in ["master-candidate", "drained", "offline"]:
241
    for value in ["yes", "no"]:
242
      AssertCommand(["gnt-node", "modify", "--force",
243
                     "--%s=%s" % (flag, value), node.primary])
244

    
245
  AssertCommand(["gnt-node", "modify", "--master-candidate=yes",
246
                 "--auto-promote", node.primary])
247

    
248
  # Test setting secondary IP address
249
  AssertCommand(["gnt-node", "modify", "--secondary-ip=%s" % node.secondary,
250
                 node.primary])
251

    
252

    
253
def _CreateOobScriptStructure():
254
  """Create a simple OOB handling script and its structure."""
255
  master = qa_config.GetMasterNode()
256

    
257
  data_path = qa_utils.UploadData(master.primary, "")
258
  verify_path = qa_utils.UploadData(master.primary, "")
259
  exit_code_path = qa_utils.UploadData(master.primary, "")
260

    
261
  oob_script = (("#!/bin/bash\n"
262
                 "echo \"$@\" > %s\n"
263
                 "cat %s\n"
264
                 "exit $(< %s)\n") %
265
                (utils.ShellQuote(verify_path), utils.ShellQuote(data_path),
266
                 utils.ShellQuote(exit_code_path)))
267
  oob_path = qa_utils.UploadData(master.primary, oob_script, mode=0700)
268

    
269
  return [oob_path, verify_path, data_path, exit_code_path]
270

    
271

    
272
def _UpdateOobFile(path, data):
273
  """Updates the data file with data."""
274
  master = qa_config.GetMasterNode()
275
  qa_utils.UploadData(master.primary, data, filename=path)
276

    
277

    
278
def _AssertOobCall(verify_path, expected_args):
279
  """Assert the OOB call was performed with expetected args."""
280
  master = qa_config.GetMasterNode()
281

    
282
  verify_output_cmd = utils.ShellQuoteArgs(["cat", verify_path])
283
  output = qa_utils.GetCommandOutput(master.primary, verify_output_cmd,
284
                                     tty=False)
285

    
286
  AssertEqual(expected_args, output.strip())
287

    
288

    
289
def TestOutOfBand():
290
  """gnt-node power"""
291
  master = qa_config.GetMasterNode()
292

    
293
  node = qa_config.AcquireNode(exclude=master)
294

    
295
  master_name = master.primary
296
  node_name = node.primary
297
  full_node_name = qa_utils.ResolveNodeName(node)
298

    
299
  (oob_path, verify_path,
300
   data_path, exit_code_path) = _CreateOobScriptStructure()
301

    
302
  try:
303
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
304
                   "oob_program=%s" % oob_path])
305

    
306
    # No data, exit 0
307
    _UpdateOobFile(exit_code_path, "0")
308

    
309
    AssertCommand(["gnt-node", "power", "on", node_name])
310
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
311

    
312
    AssertCommand(["gnt-node", "power", "-f", "off", node_name])
313
    _AssertOobCall(verify_path, "power-off %s" % full_node_name)
314

    
315
    # Power off on master without options should fail
316
    AssertCommand(["gnt-node", "power", "-f", "off", master_name], fail=True)
317
    # With force master it should still fail
318
    AssertCommand(["gnt-node", "power", "-f", "--ignore-status", "off",
319
                   master_name],
320
                  fail=True)
321

    
322
    # Verify we can't transform back to online when not yet powered on
323
    AssertCommand(["gnt-node", "modify", "-O", "no", node_name],
324
                  fail=True)
325
    # Now reset state
326
    AssertCommand(["gnt-node", "modify", "-O", "no", "--node-powered", "yes",
327
                   node_name])
328

    
329
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name])
330
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
331

    
332
    # Those commands should fail as they expect output which isn't provided yet
333
    # But they should have called the oob helper nevermind
334
    AssertCommand(["gnt-node", "power", "status", node_name],
335
                  fail=True)
336
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
337

    
338
    AssertCommand(["gnt-node", "health", node_name],
339
                  fail=True)
340
    _AssertOobCall(verify_path, "health %s" % full_node_name)
341

    
342
    AssertCommand(["gnt-node", "health"], fail=True)
343

    
344
    # Correct Data, exit 0
345
    _UpdateOobFile(data_path, serializer.DumpJson({"powered": True}))
346

    
347
    AssertCommand(["gnt-node", "power", "status", node_name])
348
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
349

    
350
    _UpdateOobFile(data_path, serializer.DumpJson([["temp", "OK"],
351
                                                   ["disk0", "CRITICAL"]]))
352

    
353
    AssertCommand(["gnt-node", "health", node_name])
354
    _AssertOobCall(verify_path, "health %s" % full_node_name)
355

    
356
    AssertCommand(["gnt-node", "health"])
357

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

    
362
    try:
363
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
364
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
365
    finally:
366
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
367

    
368
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
369
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
370

    
371
    # Data, exit 1 (all should fail)
372
    _UpdateOobFile(exit_code_path, "1")
373

    
374
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
375
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
376

    
377
    try:
378
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
379
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
380
    finally:
381
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
382

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

    
386
    AssertCommand(["gnt-node", "power", "status", node_name],
387
                  fail=True)
388
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
389

    
390
    AssertCommand(["gnt-node", "health", node_name],
391
                  fail=True)
392
    _AssertOobCall(verify_path, "health %s" % full_node_name)
393

    
394
    AssertCommand(["gnt-node", "health"], fail=True)
395

    
396
    # No data, exit 1 (all should fail)
397
    _UpdateOobFile(data_path, "")
398
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
399
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
400

    
401
    try:
402
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
403
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
404
    finally:
405
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
406

    
407
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
408
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
409

    
410
    AssertCommand(["gnt-node", "power", "status", node_name],
411
                  fail=True)
412
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
413

    
414
    AssertCommand(["gnt-node", "health", node_name],
415
                  fail=True)
416
    _AssertOobCall(verify_path, "health %s" % full_node_name)
417

    
418
    AssertCommand(["gnt-node", "health"], fail=True)
419

    
420
    # Different OOB script for node
421
    verify_path2 = qa_utils.UploadData(master.primary, "")
422
    oob_script = ("#!/bin/sh\n"
423
                  "echo \"$@\" > %s\n") % verify_path2
424
    oob_path2 = qa_utils.UploadData(master.primary, oob_script, mode=0700)
425

    
426
    try:
427
      AssertCommand(["gnt-node", "modify", "--node-parameters",
428
                     "oob_program=%s" % oob_path2, node_name])
429
      AssertCommand(["gnt-node", "power", "on", node_name])
430
      _AssertOobCall(verify_path2, "power-on %s" % full_node_name)
431
    finally:
432
      AssertCommand(["gnt-node", "modify", "--node-parameters",
433
                     "oob_program=default", node_name])
434
      AssertCommand(["rm", "-f", oob_path2, verify_path2])
435
  finally:
436
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
437
                   "oob_program="])
438
    AssertCommand(["rm", "-f", oob_path, verify_path, data_path,
439
                   exit_code_path])
440

    
441

    
442
def TestNodeList():
443
  """gnt-node list"""
444
  qa_utils.GenericQueryTest("gnt-node", query.NODE_FIELDS.keys())
445

    
446

    
447
def TestNodeListFields():
448
  """gnt-node list-fields"""
449
  qa_utils.GenericQueryFieldsTest("gnt-node", query.NODE_FIELDS.keys())
450

    
451

    
452
def TestNodeListDrbd(node):
453
  """gnt-node list-drbd"""
454
  AssertCommand(["gnt-node", "list-drbd", node.primary])
455

    
456

    
457
def _BuildSetESCmd(action, value, node_name):
458
  cmd = ["gnt-node"]
459
  if action == "add":
460
    cmd.extend(["add", "--readd"])
461
  else:
462
    cmd.append("modify")
463
  cmd.extend(["--node-parameters", "exclusive_storage=%s" % value, node_name])
464
  return cmd
465

    
466

    
467
def TestExclStorSingleNode(node):
468
  """gnt-node add/modify cannot change the exclusive_storage flag.
469

470
  """
471
  for action in ["add", "modify"]:
472
    for value in (True, False, "default"):
473
      AssertCommand(_BuildSetESCmd(action, value, node.primary), fail=True)