Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ 3388debb

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):
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.STORAGE_TYPES
118
  # as soon as they are implemented.
119
  enabled_storage_types = qa_config.GetEnabledStorageTypes()
120
  testable_storage_types = list(set(enabled_storage_types).intersection(
121
      set([constants.ST_FILE, constants.ST_LVM_VG, constants.ST_LVM_PV])))
122

    
123
  for storage_type in testable_storage_types:
124

    
125
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type]
126

    
127
    # Test simple list
128
    AssertCommand(cmd)
129

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

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

    
142
    # Test with up to two devices
143
    testdevcount = 2
144

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

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

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

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

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

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

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

    
184

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

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

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

    
198

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

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

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

    
212

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

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

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

    
232

    
233
def TestNodeModify(node):
234
  """gnt-node modify"""
235

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

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

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

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

    
253

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

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

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

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

    
272

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

    
278

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

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

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

    
289

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
442

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

    
447

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

    
452

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

    
457

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

    
467

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

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