Statistics
| Branch: | Tag: | Revision:

root / qa / qa_node.py @ abb24834

History | View | Annotate | Download (11.8 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007 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
from ganeti import utils
23
from ganeti import constants
24
from ganeti import query
25
from ganeti import serializer
26

    
27
import qa_config
28
import qa_error
29
import qa_utils
30

    
31
from qa_utils import AssertCommand
32

    
33

    
34
def _NodeAdd(node, readd=False):
35
  if not readd and node.get('_added', False):
36
    raise qa_error.Error("Node %s already in cluster" % node['primary'])
37
  elif readd and not node.get('_added', False):
38
    raise qa_error.Error("Node %s not yet in cluster" % node['primary'])
39

    
40
  cmd = ['gnt-node', 'add', "--no-ssh-key-check"]
41
  if node.get('secondary', None):
42
    cmd.append('--secondary-ip=%s' % node['secondary'])
43
  if readd:
44
    cmd.append('--readd')
45
  cmd.append(node['primary'])
46

    
47
  AssertCommand(cmd)
48

    
49
  node['_added'] = True
50

    
51

    
52
def _NodeRemove(node):
53
  AssertCommand(["gnt-node", "remove", node["primary"]])
54
  node['_added'] = False
55

    
56

    
57
def TestNodeAddAll():
58
  """Adding all nodes to cluster."""
59
  master = qa_config.GetMasterNode()
60
  for node in qa_config.get('nodes'):
61
    if node != master:
62
      _NodeAdd(node, readd=False)
63

    
64

    
65
def MarkNodeAddedAll():
66
  """Mark all nodes as added.
67

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

70
  """
71
  master = qa_config.GetMasterNode()
72
  for node in qa_config.get('nodes'):
73
    if node != master:
74
      node['_added'] = True
75

    
76

    
77
def TestNodeRemoveAll():
78
  """Removing all nodes from cluster."""
79
  master = qa_config.GetMasterNode()
80
  for node in qa_config.get('nodes'):
81
    if node != master:
82
      _NodeRemove(node)
83

    
84

    
85
def TestNodeReadd(node):
86
  """gnt-node add --readd"""
87
  _NodeAdd(node, readd=True)
88

    
89

    
90
def TestNodeInfo():
91
  """gnt-node info"""
92
  AssertCommand(["gnt-node", "info"])
93

    
94

    
95
def TestNodeVolumes():
96
  """gnt-node volumes"""
97
  AssertCommand(["gnt-node", "volumes"])
98

    
99

    
100
def TestNodeStorage():
101
  """gnt-node storage"""
102
  master = qa_config.GetMasterNode()
103

    
104
  for storage_type in constants.VALID_STORAGE_TYPES:
105
    # Test simple list
106
    AssertCommand(["gnt-node", "list-storage", "--storage-type", storage_type])
107

    
108
    # Test all storage fields
109
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
110
           "--output=%s" % ",".join(list(constants.VALID_STORAGE_FIELDS) +
111
                                    [constants.SF_NODE, constants.SF_TYPE])]
112
    AssertCommand(cmd)
113

    
114
    # Get list of valid storage devices
115
    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
116
           "--output=node,name,allocatable", "--separator=|",
117
           "--no-headers"]
118
    output = qa_utils.GetCommandOutput(master["primary"],
119
                                       utils.ShellQuoteArgs(cmd))
120

    
121
    # Test with up to two devices
122
    testdevcount = 2
123

    
124
    for line in output.splitlines()[:testdevcount]:
125
      (node_name, st_name, st_allocatable) = line.split("|")
126

    
127
      # Dummy modification without any changes
128
      cmd = ["gnt-node", "modify-storage", node_name, storage_type, st_name]
129
      AssertCommand(cmd)
130

    
131
      # Make sure we end up with the same value as before
132
      if st_allocatable.lower() == "y":
133
        test_allocatable = ["no", "yes"]
134
      else:
135
        test_allocatable = ["yes", "no"]
136

    
137
      fail = (constants.SF_ALLOCATABLE not in
138
              constants.MODIFIABLE_STORAGE_FIELDS.get(storage_type, []))
139

    
140
      for i in test_allocatable:
141
        AssertCommand(["gnt-node", "modify-storage", "--allocatable", i,
142
                       node_name, storage_type, st_name], fail=fail)
143

    
144
      # Test repair functionality
145
      fail = (constants.SO_FIX_CONSISTENCY not in
146
              constants.VALID_STORAGE_OPERATIONS.get(storage_type, []))
147
      AssertCommand(["gnt-node", "repair-storage", node_name,
148
                     storage_type, st_name], fail=fail)
149

    
150

    
151
def TestNodeFailover(node, node2):
152
  """gnt-node failover"""
153
  if qa_utils.GetNodeInstances(node2, secondaries=False):
154
    raise qa_error.UnusableNodeError("Secondary node has at least one"
155
                                     " primary instance. This test requires"
156
                                     " it to have no primary instances.")
157

    
158
  # Fail over to secondary node
159
  AssertCommand(["gnt-node", "failover", "-f", node["primary"]])
160

    
161
  # ... and back again.
162
  AssertCommand(["gnt-node", "failover", "-f", node2["primary"]])
163

    
164

    
165
def TestNodeEvacuate(node, node2):
166
  """gnt-node evacuate"""
167
  node3 = qa_config.AcquireNode(exclude=[node, node2])
168
  try:
169
    if qa_utils.GetNodeInstances(node3, secondaries=True):
170
      raise qa_error.UnusableNodeError("Evacuation node has at least one"
171
                                       " secondary instance. This test requires"
172
                                       " it to have no secondary instances.")
173

    
174
    # Evacuate all secondary instances
175
    AssertCommand(["gnt-node", "evacuate", "-f",
176
                   "--new-secondary=%s" % node3["primary"], node2["primary"]])
177

    
178
    # ... and back again.
179
    AssertCommand(["gnt-node", "evacuate", "-f",
180
                   "--new-secondary=%s" % node2["primary"], node3["primary"]])
181
  finally:
182
    qa_config.ReleaseNode(node3)
183

    
184

    
185
def TestNodeModify(node):
186
  """gnt-node modify"""
187
  for flag in ["master-candidate", "drained", "offline"]:
188
    for value in ["yes", "no"]:
189
      AssertCommand(["gnt-node", "modify", "--force",
190
                     "--%s=%s" % (flag, value), node["primary"]])
191

    
192
  AssertCommand(["gnt-node", "modify", "--master-candidate=yes",
193
                 "--auto-promote", node["primary"]])
194

    
195

    
196
def _CreateOobScriptStructure():
197
  """Create a simple OOB handling script and its structure."""
198
  master = qa_config.GetMasterNode()
199

    
200
  data_path = qa_utils.UploadData(master["primary"], "")
201
  verify_path = qa_utils.UploadData(master["primary"], "")
202
  exit_code_path = qa_utils.UploadData(master["primary"], "")
203

    
204
  oob_script = (("#!/bin/bash\n"
205
                 "echo \"$@\" > %s\n"
206
                 "cat %s\n"
207
                 "exit $(< %s)\n") %
208
                (utils.ShellQuote(verify_path), utils.ShellQuote(data_path),
209
                 utils.ShellQuote(exit_code_path)))
210
  oob_path = qa_utils.UploadData(master["primary"], oob_script, mode=0700)
211

    
212
  return [oob_path, verify_path, data_path, exit_code_path]
213

    
214

    
215
def _UpdateOobFile(path, data):
216
  """Updates the data file with data."""
217
  master = qa_config.GetMasterNode()
218
  qa_utils.UploadData(master["primary"], data, filename=path)
219

    
220

    
221
def _AssertOobCall(verify_path, expected_args):
222
  """Assert the OOB call was performed with expetected args."""
223
  master = qa_config.GetMasterNode()
224

    
225
  verify_output_cmd = utils.ShellQuoteArgs(["cat", verify_path])
226
  output = qa_utils.GetCommandOutput(master["primary"], verify_output_cmd)
227

    
228
  qa_utils.AssertEqual(expected_args, output.strip())
229

    
230

    
231
def TestOutOfBand():
232
  """gnt-node power"""
233
  master = qa_config.GetMasterNode()
234

    
235
  # Find first non master node for tests
236
  for node in qa_config.get('nodes'):
237
    if node != master:
238
      node_name = node["primary"]
239
      break
240

    
241
  (oob_path, verify_path,
242
   data_path, exit_code_path) = _CreateOobScriptStructure()
243

    
244
  try:
245
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
246
                   "oob_program=%s" % oob_path])
247

    
248
    # No data, exit 0
249
    _UpdateOobFile(exit_code_path, "0")
250

    
251
    AssertCommand(["gnt-node", "power", "on", node_name])
252
    _AssertOobCall(verify_path, "power-on %s" % node_name)
253

    
254
    AssertCommand(["gnt-node", "power", "off", node_name])
255
    _AssertOobCall(verify_path, "power-off %s" % node_name)
256

    
257
    # Verify we can't transform back to online when not yet powered on
258
    AssertCommand(["gnt-node", "modify", "-O", "no", node_name],
259
                  fail=True)
260
    # Now reset state
261
    AssertCommand(["gnt-node", "modify", "-O", "no", "--node-powered", "yes",
262
                   node_name])
263

    
264
    AssertCommand(["gnt-node", "power", "cycle", node_name])
265
    _AssertOobCall(verify_path, "power-cycle %s" % node_name)
266

    
267
    # This command should fail as it expects output which isn't provided yet
268
    # But it should have called the oob helper nevermind
269
    AssertCommand(["gnt-node", "power", "status", node_name],
270
                  fail=True)
271
    _AssertOobCall(verify_path, "power-status %s" % node_name)
272

    
273
    # Data, exit 0
274
    _UpdateOobFile(data_path, serializer.DumpJson({ "powered": True }))
275

    
276
    AssertCommand(["gnt-node", "power", "status", node_name])
277
    _AssertOobCall(verify_path, "power-status %s" % node_name)
278

    
279
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
280
    _AssertOobCall(verify_path, "power-on %s" % node_name)
281

    
282
    try:
283
      AssertCommand(["gnt-node", "power", "off", node_name], fail=True)
284
      _AssertOobCall(verify_path, "power-off %s" % node_name)
285
    finally:
286
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
287

    
288
    AssertCommand(["gnt-node", "power", "cycle", node_name], fail=True)
289
    _AssertOobCall(verify_path, "power-cycle %s" % node_name)
290

    
291
    # Data, exit 1 (all should fail)
292
    _UpdateOobFile(exit_code_path, "1")
293

    
294
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
295
    _AssertOobCall(verify_path, "power-on %s" % node_name)
296

    
297
    try:
298
      AssertCommand(["gnt-node", "power", "off", node_name], fail=True)
299
      _AssertOobCall(verify_path, "power-off %s" % node_name)
300
    finally:
301
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
302

    
303
    AssertCommand(["gnt-node", "power", "cycle", node_name], fail=True)
304
    _AssertOobCall(verify_path, "power-cycle %s" % node_name)
305

    
306
    AssertCommand(["gnt-node", "power", "status", node_name],
307
                  fail=True)
308
    _AssertOobCall(verify_path, "power-status %s" % node_name)
309

    
310
    # No data, exit 1 (all should fail)
311
    _UpdateOobFile(data_path, "")
312
    AssertCommand(["gnt-node", "power", "on", node_name], fail=True)
313
    _AssertOobCall(verify_path, "power-on %s" % node_name)
314

    
315
    try:
316
      AssertCommand(["gnt-node", "power", "off", node_name], fail=True)
317
      _AssertOobCall(verify_path, "power-off %s" % node_name)
318
    finally:
319
      AssertCommand(["gnt-node", "modify", "-O", "no", node_name])
320

    
321
    AssertCommand(["gnt-node", "power", "cycle", node_name], fail=True)
322
    _AssertOobCall(verify_path, "power-cycle %s" % node_name)
323

    
324
    AssertCommand(["gnt-node", "power", "status", node_name],
325
                  fail=True)
326
    _AssertOobCall(verify_path, "power-status %s" % node_name)
327

    
328
    # Different OOB script for node
329
    verify_path2 = qa_utils.UploadData(master["primary"], "")
330
    oob_script = ("#!/bin/sh\n"
331
                  "echo \"$@\" > %s\n") % verify_path2
332
    oob_path2 = qa_utils.UploadData(master["primary"], oob_script, mode=0700)
333

    
334
    try:
335
      AssertCommand(["gnt-node", "modify", "--node-parameters",
336
                     "oob_program=%s" % oob_path2, node_name])
337
      AssertCommand(["gnt-node", "power", "on", node_name])
338
      _AssertOobCall(verify_path2, "power-on %s" % node_name)
339
    finally:
340
      AssertCommand(["gnt-node", "modify", "--node-parameters",
341
                     "oob_program=default", node_name])
342
      AssertCommand(["rm", "-f", oob_path2, verify_path2])
343
  finally:
344
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
345
                   "oob_program=default"])
346
    AssertCommand(["rm", "-f", oob_path, verify_path, data_path,
347
                   exit_code_path])
348

    
349

    
350
def TestNodeList():
351
  """gnt-node list"""
352
  qa_utils.GenericQueryTest("gnt-node", query.NODE_FIELDS.keys())
353

    
354

    
355
def TestNodeListFields():
356
  """gnt-node list-fields"""
357
  qa_utils.GenericQueryFieldsTest("gnt-node", query.NODE_FIELDS.keys())