Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ a07ae57f

History | View | Annotate | Download (15.8 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, group=None):
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
  if group is not None:
50
    cmd.extend(["--node-group", group])
51
  cmd.append(node.primary)
52

    
53
  AssertCommand(cmd)
54

    
55
  if readd:
56
    assert node.added
57
  else:
58
    node.MarkAdded()
59

    
60

    
61
def NodeRemove(node):
62
  AssertCommand(["gnt-node", "remove", node.primary])
63
  node.MarkRemoved()
64

    
65

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

    
71

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

    
79

    
80
def MarkNodeAddedAll():
81
  """Mark all nodes as added.
82

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

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

    
91

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

    
99

    
100
def TestNodeReadd(node):
101
  """gnt-node add --readd"""
102
  NodeAdd(node, readd=True)
103

    
104

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

    
109

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

    
114

    
115
def TestNodeStorage():
116
  """gnt-node storage"""
117
  master = qa_config.GetMasterNode()
118

    
119
  # FIXME: test all storage_types in constants.STORAGE_TYPES
120
  # as soon as they are implemented.
121
  enabled_storage_types = qa_config.GetEnabledStorageTypes()
122
  testable_storage_types = list(set(enabled_storage_types).intersection(
123
      set([constants.ST_FILE, constants.ST_LVM_VG, constants.ST_LVM_PV])))
124

    
125
  for storage_type in testable_storage_types:
126

    
127
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type]
128

    
129
    # Test simple list
130
    AssertCommand(cmd)
131

    
132
    # Test all storage fields
133
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
134
           "--output=%s" % ",".join(list(constants.VALID_STORAGE_FIELDS))]
135
    AssertCommand(cmd)
136

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

    
144
    # Test with up to two devices
145
    testdevcount = 2
146

    
147
    for line in output.splitlines()[:testdevcount]:
148
      (node_name, st_name, st_allocatable) = line.split("|")
149

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

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

    
160
      fail = (constants.SF_ALLOCATABLE not in
161
              constants.MODIFIABLE_STORAGE_FIELDS.get(storage_type, []))
162

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

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

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

    
186

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

    
194
  # Fail over to secondary node
195
  AssertCommand(["gnt-node", "failover", "-f", node.primary])
196

    
197
  # ... and back again.
198
  AssertCommand(["gnt-node", "failover", "-f", node2.primary])
199

    
200

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

    
208
  # Migrate to secondary node
209
  AssertCommand(["gnt-node", "migrate", "-f", node.primary])
210

    
211
  # ... and back again.
212
  AssertCommand(["gnt-node", "migrate", "-f", node2.primary])
213

    
214

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

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

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

    
234

    
235
def TestNodeModify(node):
236
  """gnt-node modify"""
237

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

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

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

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

    
255

    
256
def _CreateOobScriptStructure():
257
  """Create a simple OOB handling script and its structure."""
258
  master = qa_config.GetMasterNode()
259

    
260
  data_path = qa_utils.UploadData(master.primary, "")
261
  verify_path = qa_utils.UploadData(master.primary, "")
262
  exit_code_path = qa_utils.UploadData(master.primary, "")
263

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

    
272
  return [oob_path, verify_path, data_path, exit_code_path]
273

    
274

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

    
280

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

    
285
  verify_output_cmd = utils.ShellQuoteArgs(["cat", verify_path])
286
  output = qa_utils.GetCommandOutput(master.primary, verify_output_cmd,
287
                                     tty=False)
288

    
289
  AssertEqual(expected_args, output.strip())
290

    
291

    
292
def TestOutOfBand():
293
  """gnt-node power"""
294
  master = qa_config.GetMasterNode()
295

    
296
  node = qa_config.AcquireNode(exclude=master)
297

    
298
  master_name = master.primary
299
  node_name = node.primary
300
  full_node_name = qa_utils.ResolveNodeName(node)
301

    
302
  (oob_path, verify_path,
303
   data_path, exit_code_path) = _CreateOobScriptStructure()
304

    
305
  try:
306
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
307
                   "oob_program=%s" % oob_path])
308

    
309
    # No data, exit 0
310
    _UpdateOobFile(exit_code_path, "0")
311

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

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

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

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

    
332
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name])
333
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
334

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

    
341
    AssertCommand(["gnt-node", "health", node_name],
342
                  fail=True)
343
    _AssertOobCall(verify_path, "health %s" % full_node_name)
344

    
345
    AssertCommand(["gnt-node", "health"], fail=True)
346

    
347
    # Correct Data, exit 0
348
    _UpdateOobFile(data_path, serializer.DumpJson({"powered": True}))
349

    
350
    AssertCommand(["gnt-node", "power", "status", node_name])
351
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
352

    
353
    _UpdateOobFile(data_path, serializer.DumpJson([["temp", "OK"],
354
                                                   ["disk0", "CRITICAL"]]))
355

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

    
359
    AssertCommand(["gnt-node", "health"])
360

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

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

    
371
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
372
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
373

    
374
    # Data, exit 1 (all should fail)
375
    _UpdateOobFile(exit_code_path, "1")
376

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

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

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

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

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

    
397
    AssertCommand(["gnt-node", "health"], fail=True)
398

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

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

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

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

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

    
421
    AssertCommand(["gnt-node", "health"], fail=True)
422

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

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

    
444

    
445
def TestNodeList():
446
  """gnt-node list"""
447
  qa_utils.GenericQueryTest("gnt-node", query.NODE_FIELDS.keys())
448

    
449

    
450
def TestNodeListFields():
451
  """gnt-node list-fields"""
452
  qa_utils.GenericQueryFieldsTest("gnt-node", query.NODE_FIELDS.keys())
453

    
454

    
455
def TestNodeListDrbd(node):
456
  """gnt-node list-drbd"""
457
  AssertCommand(["gnt-node", "list-drbd", node.primary])
458

    
459

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

    
469

    
470
def TestExclStorSingleNode(node):
471
  """gnt-node add/modify cannot change the exclusive_storage flag.
472

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