Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ 3c87d614

History | View | Annotate | Download (15.2 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.get("_added", False):
40
    raise qa_error.Error("Node %s already in cluster" % node["primary"])
41
  elif readd and not node.get("_added", False):
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.get("secondary", None):
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
  node["_added"] = True
54

    
55

    
56
def _NodeRemove(node):
57
  AssertCommand(["gnt-node", "remove", node["primary"]])
58
  node["_added"] = False
59

    
60

    
61
def MakeNodeOffline(node, value):
62
  """gnt-node modify --offline=value"""
63
  # value in ["yes", "no"]
64
  AssertCommand(["gnt-node", "modify", "--offline", value, node["primary"]])
65

    
66

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

    
74

    
75
def MarkNodeAddedAll():
76
  """Mark all nodes as added.
77

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

80
  """
81
  master = qa_config.GetMasterNode()
82
  for node in qa_config.get("nodes"):
83
    if node != master:
84
      node["_added"] = True
85

    
86

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

    
94

    
95
def TestNodeReadd(node):
96
  """gnt-node add --readd"""
97
  _NodeAdd(node, readd=True)
98

    
99

    
100
def TestNodeInfo():
101
  """gnt-node info"""
102
  AssertCommand(["gnt-node", "info"])
103

    
104

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

    
109

    
110
def TestNodeStorage():
111
  """gnt-node storage"""
112
  master = qa_config.GetMasterNode()
113

    
114
  for storage_type in constants.VALID_STORAGE_TYPES:
115

    
116
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type]
117

    
118
    # Skip file storage if not enabled, otherwise QA will fail; we
119
    # just test for basic failure, but otherwise skip the rest of the
120
    # tests
121
    if storage_type == constants.ST_FILE and not constants.ENABLE_FILE_STORAGE:
122
      AssertCommand(cmd, fail=True)
123
      continue
124

    
125
    # Test simple list
126
    AssertCommand(cmd)
127

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

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

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

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

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

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

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

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

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

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

    
183

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

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

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

    
197

    
198
def TestNodeEvacuate(node, node2):
199
  """gnt-node evacuate"""
200
  node3 = qa_config.AcquireNode(exclude=[node, node2])
201
  try:
202
    if qa_utils.GetNodeInstances(node3, secondaries=True):
203
      raise qa_error.UnusableNodeError("Evacuation node has at least one"
204
                                       " secondary instance. This test requires"
205
                                       " it to have no secondary instances.")
206

    
207
    # Evacuate all secondary instances
208
    AssertCommand(["gnt-node", "evacuate", "-f",
209
                   "--new-secondary=%s" % node3["primary"], node2["primary"]])
210

    
211
    # ... and back again.
212
    AssertCommand(["gnt-node", "evacuate", "-f",
213
                   "--new-secondary=%s" % node2["primary"], node3["primary"]])
214
  finally:
215
    qa_config.ReleaseNode(node3)
216

    
217

    
218
def TestNodeModify(node):
219
  """gnt-node modify"""
220
  for flag in ["master-candidate", "drained", "offline"]:
221
    for value in ["yes", "no"]:
222
      AssertCommand(["gnt-node", "modify", "--force",
223
                     "--%s=%s" % (flag, value), node["primary"]])
224

    
225
  AssertCommand(["gnt-node", "modify", "--master-candidate=yes",
226
                 "--auto-promote", node["primary"]])
227

    
228
  # Test setting secondary IP address
229
  AssertCommand(["gnt-node", "modify", "--secondary-ip=%s" % node["secondary"],
230
                 node["primary"]])
231

    
232

    
233
def _CreateOobScriptStructure():
234
  """Create a simple OOB handling script and its structure."""
235
  master = qa_config.GetMasterNode()
236

    
237
  data_path = qa_utils.UploadData(master["primary"], "")
238
  verify_path = qa_utils.UploadData(master["primary"], "")
239
  exit_code_path = qa_utils.UploadData(master["primary"], "")
240

    
241
  oob_script = (("#!/bin/bash\n"
242
                 "echo \"$@\" > %s\n"
243
                 "cat %s\n"
244
                 "exit $(< %s)\n") %
245
                (utils.ShellQuote(verify_path), utils.ShellQuote(data_path),
246
                 utils.ShellQuote(exit_code_path)))
247
  oob_path = qa_utils.UploadData(master["primary"], oob_script, mode=0700)
248

    
249
  return [oob_path, verify_path, data_path, exit_code_path]
250

    
251

    
252
def _UpdateOobFile(path, data):
253
  """Updates the data file with data."""
254
  master = qa_config.GetMasterNode()
255
  qa_utils.UploadData(master["primary"], data, filename=path)
256

    
257

    
258
def _AssertOobCall(verify_path, expected_args):
259
  """Assert the OOB call was performed with expetected args."""
260
  master = qa_config.GetMasterNode()
261

    
262
  verify_output_cmd = utils.ShellQuoteArgs(["cat", verify_path])
263
  output = qa_utils.GetCommandOutput(master["primary"], verify_output_cmd,
264
                                     tty=False)
265

    
266
  AssertEqual(expected_args, output.strip())
267

    
268

    
269
def TestOutOfBand():
270
  """gnt-node power"""
271
  master = qa_config.GetMasterNode()
272

    
273
  node = qa_config.AcquireNode(exclude=master)
274

    
275
  master_name = master["primary"]
276
  node_name = node["primary"]
277
  full_node_name = qa_utils.ResolveNodeName(node)
278

    
279
  (oob_path, verify_path,
280
   data_path, exit_code_path) = _CreateOobScriptStructure()
281

    
282
  try:
283
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
284
                   "oob_program=%s" % oob_path])
285

    
286
    # No data, exit 0
287
    _UpdateOobFile(exit_code_path, "0")
288

    
289
    AssertCommand(["gnt-node", "power", "on", node_name])
290
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
291

    
292
    AssertCommand(["gnt-node", "power", "-f", "off", node_name])
293
    _AssertOobCall(verify_path, "power-off %s" % full_node_name)
294

    
295
    # Power off on master without options should fail
296
    AssertCommand(["gnt-node", "power", "-f", "off", master_name], fail=True)
297
    # With force master it should still fail
298
    AssertCommand(["gnt-node", "power", "-f", "--ignore-status", "off",
299
                   master_name],
300
                  fail=True)
301

    
302
    # Verify we can't transform back to online when not yet powered on
303
    AssertCommand(["gnt-node", "modify", "-O", "no", node_name],
304
                  fail=True)
305
    # Now reset state
306
    AssertCommand(["gnt-node", "modify", "-O", "no", "--node-powered", "yes",
307
                   node_name])
308

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

    
312
    # Those commands should fail as they expect output which isn't provided yet
313
    # But they should have called the oob helper nevermind
314
    AssertCommand(["gnt-node", "power", "status", node_name],
315
                  fail=True)
316
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
317

    
318
    AssertCommand(["gnt-node", "health", node_name],
319
                  fail=True)
320
    _AssertOobCall(verify_path, "health %s" % full_node_name)
321

    
322
    AssertCommand(["gnt-node", "health"], fail=True)
323

    
324
    # Correct Data, exit 0
325
    _UpdateOobFile(data_path, serializer.DumpJson({"powered": True}))
326

    
327
    AssertCommand(["gnt-node", "power", "status", node_name])
328
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
329

    
330
    _UpdateOobFile(data_path, serializer.DumpJson([["temp", "OK"],
331
                                                   ["disk0", "CRITICAL"]]))
332

    
333
    AssertCommand(["gnt-node", "health", node_name])
334
    _AssertOobCall(verify_path, "health %s" % full_node_name)
335

    
336
    AssertCommand(["gnt-node", "health"])
337

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

    
342
    try:
343
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
344
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
345
    finally:
346
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
347

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

    
351
    # Data, exit 1 (all should fail)
352
    _UpdateOobFile(exit_code_path, "1")
353

    
354
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
355
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
356

    
357
    try:
358
      AssertCommand(["gnt-node", "power", "-f", "off", node_name], fail=True)
359
      _AssertOobCall(verify_path, "power-off %s" % full_node_name)
360
    finally:
361
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
362

    
363
    AssertCommand(["gnt-node", "power", "-f", "cycle", node_name], fail=True)
364
    _AssertOobCall(verify_path, "power-cycle %s" % full_node_name)
365

    
366
    AssertCommand(["gnt-node", "power", "status", node_name],
367
                  fail=True)
368
    _AssertOobCall(verify_path, "power-status %s" % full_node_name)
369

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

    
374
    AssertCommand(["gnt-node", "health"], fail=True)
375

    
376
    # No data, exit 1 (all should fail)
377
    _UpdateOobFile(data_path, "")
378
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
379
    _AssertOobCall(verify_path, "power-on %s" % full_node_name)
380

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

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

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

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

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

    
400
    # Different OOB script for node
401
    verify_path2 = qa_utils.UploadData(master["primary"], "")
402
    oob_script = ("#!/bin/sh\n"
403
                  "echo \"$@\" > %s\n") % verify_path2
404
    oob_path2 = qa_utils.UploadData(master["primary"], oob_script, mode=0700)
405

    
406
    try:
407
      AssertCommand(["gnt-node", "modify", "--node-parameters",
408
                     "oob_program=%s" % oob_path2, node_name])
409
      AssertCommand(["gnt-node", "power", "on", node_name])
410
      _AssertOobCall(verify_path2, "power-on %s" % full_node_name)
411
    finally:
412
      AssertCommand(["gnt-node", "modify", "--node-parameters",
413
                     "oob_program=default", node_name])
414
      AssertCommand(["rm", "-f", oob_path2, verify_path2])
415
  finally:
416
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
417
                   "oob_program="])
418
    AssertCommand(["rm", "-f", oob_path, verify_path, data_path,
419
                   exit_code_path])
420

    
421

    
422
def TestNodeList():
423
  """gnt-node list"""
424
  qa_utils.GenericQueryTest("gnt-node", query.NODE_FIELDS.keys())
425

    
426

    
427
def TestNodeListFields():
428
  """gnt-node list-fields"""
429
  qa_utils.GenericQueryFieldsTest("gnt-node", query.NODE_FIELDS.keys())
430

    
431

    
432
def TestNodeListDrbd(node):
433
  """gnt-node list-drbd"""
434
  AssertCommand(["gnt-node", "list-drbd", node["primary"]])
435

    
436

    
437
def _BuildSetESCmd(action, value, node_name):
438
  cmd = ["gnt-node"]
439
  if action == "add":
440
    cmd.extend(["add", "--readd"])
441
  else:
442
    cmd.append("modify")
443
  cmd.extend(["--node-parameters", "exclusive_storage=%s" % value, node_name])
444
  return cmd
445

    
446

    
447
def TestExclStorSingleNode(node):
448
  """gnt-node add/modify cannot change the exclusive_storage flag.
449

450
  """
451
  for action in ["add", "modify"]:
452
    for value in (True, False, "default"):
453
      AssertCommand(_BuildSetESCmd(action, value, node["primary"]), fail=True)