Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ c632d3a5

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

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

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

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

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

    
258

    
259
def _CreateOobScriptStructure():
260
  """Create a simple OOB handling script and its structure."""
261
  master = qa_config.GetMasterNode()
262

    
263
  data_path = qa_utils.UploadData(master.primary, "")
264
  verify_path = qa_utils.UploadData(master.primary, "")
265
  exit_code_path = qa_utils.UploadData(master.primary, "")
266

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

    
275
  return [oob_path, verify_path, data_path, exit_code_path]
276

    
277

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

    
283

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

    
288
  verify_output_cmd = utils.ShellQuoteArgs(["cat", verify_path])
289
  output = qa_utils.GetCommandOutput(master.primary, verify_output_cmd,
290
                                     tty=False)
291

    
292
  AssertEqual(expected_args, output.strip())
293

    
294

    
295
def TestOutOfBand():
296
  """gnt-node power"""
297
  master = qa_config.GetMasterNode()
298

    
299
  node = qa_config.AcquireNode(exclude=master)
300

    
301
  master_name = master.primary
302
  node_name = node.primary
303
  full_node_name = qa_utils.ResolveNodeName(node)
304

    
305
  (oob_path, verify_path,
306
   data_path, exit_code_path) = _CreateOobScriptStructure()
307

    
308
  try:
309
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
310
                   "oob_program=%s" % oob_path])
311

    
312
    # No data, exit 0
313
    _UpdateOobFile(exit_code_path, "0")
314

    
315
    AssertCommand(["gnt-node", "power", "on", node_name])
316
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
317

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

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

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

    
335
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name])
336
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
337

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

    
344
    AssertCommand(["gnt-node", "health", node_name],
345
                  fail=True)
346
    _AssertOobCall(verify_path, "health %s" % full_node_name)
347

    
348
    AssertCommand(["gnt-node", "health"], fail=True)
349

    
350
    # Correct Data, exit 0
351
    _UpdateOobFile(data_path, serializer.DumpJson({"powered": True}))
352

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

    
356
    _UpdateOobFile(data_path, serializer.DumpJson([["temp", "OK"],
357
                                                   ["disk0", "CRITICAL"]]))
358

    
359
    AssertCommand(["gnt-node", "health", node_name])
360
    _AssertOobCall(verify_path, "health %s" % full_node_name)
361

    
362
    AssertCommand(["gnt-node", "health"])
363

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

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

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

    
377
    # Data, exit 1 (all should fail)
378
    _UpdateOobFile(exit_code_path, "1")
379

    
380
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
381
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
382

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

    
389
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
390
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
391

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

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

    
400
    AssertCommand(["gnt-node", "health"], fail=True)
401

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

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

    
413
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
414
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
415

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

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

    
424
    AssertCommand(["gnt-node", "health"], fail=True)
425

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

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

    
447

    
448
def TestNodeList():
449
  """gnt-node list"""
450
  qa_utils.GenericQueryTest("gnt-node", query.NODE_FIELDS.keys())
451

    
452

    
453
def TestNodeListFields():
454
  """gnt-node list-fields"""
455
  qa_utils.GenericQueryFieldsTest("gnt-node", query.NODE_FIELDS.keys())
456

    
457

    
458
def TestNodeListDrbd(node):
459
  """gnt-node list-drbd"""
460
  AssertCommand(["gnt-node", "list-drbd", node.primary])
461

    
462

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

    
472

    
473
def TestExclStorSingleNode(node):
474
  """gnt-node add/modify cannot change the exclusive_storage flag.
475

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