Code style fixes for drbd8-upgrade tool
[ganeti-local] / qa / qa_instance.py
1 # Copyright (C) 2007 Google Inc.
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 # 02110-1301, USA.
17
18
19 """Instance related QA tests.
20
21 """
22
23 import re
24 import time
25
26 from ganeti import utils
27 from ganeti import constants
28
29 import qa_config
30 import qa_utils
31 import qa_error
32
33 from qa_utils import AssertEqual, AssertNotEqual, StartSSH
34
35
36 def _GetDiskStatePath(disk):
37   return "/sys/block/%s/device/state" % disk
38
39
40 def _GetGenericAddParameters():
41   return ['--os-size=%s' % qa_config.get('os-size'),
42           '--swap-size=%s' % qa_config.get('swap-size'),
43           '--memory=%s' % qa_config.get('mem')]
44
45
46 def _DiskTest(node, disk_template):
47   master = qa_config.GetMasterNode()
48
49   instance = qa_config.AcquireInstance()
50   try:
51     cmd = (['gnt-instance', 'add',
52             '--os-type=%s' % qa_config.get('os'),
53             '--disk-template=%s' % disk_template,
54             '--node=%s' % node] +
55            _GetGenericAddParameters())
56     cmd.append(instance['name'])
57
58     AssertEqual(StartSSH(master['primary'],
59                          utils.ShellQuoteArgs(cmd)).wait(), 0)
60     return instance
61   except:
62     qa_config.ReleaseInstance(instance)
63     raise
64
65
66 @qa_utils.DefineHook('instance-add-plain-disk')
67 def TestInstanceAddWithPlainDisk(node):
68   """gnt-instance add -t plain"""
69   return _DiskTest(node['primary'], 'plain')
70
71
72 @qa_utils.DefineHook('instance-add-local-mirror-disk')
73 def TestInstanceAddWithLocalMirrorDisk(node):
74   """gnt-instance add -t local_raid1"""
75   return _DiskTest(node['primary'], 'local_raid1')
76
77
78 @qa_utils.DefineHook('instance-add-remote-raid-disk')
79 def TestInstanceAddWithRemoteRaidDisk(node, node2):
80   """gnt-instance add -t remote_raid1"""
81   return _DiskTest("%s:%s" % (node['primary'], node2['primary']),
82                    'remote_raid1')
83
84
85 @qa_utils.DefineHook('instance-add-drbd-disk')
86 def TestInstanceAddWithDrbdDisk(node, node2):
87   """gnt-instance add -t drbd"""
88   return _DiskTest("%s:%s" % (node['primary'], node2['primary']),
89                    'drbd')
90
91
92 @qa_utils.DefineHook('instance-remove')
93 def TestInstanceRemove(instance):
94   """gnt-instance remove"""
95   master = qa_config.GetMasterNode()
96
97   cmd = ['gnt-instance', 'remove', '-f', instance['name']]
98   AssertEqual(StartSSH(master['primary'],
99                        utils.ShellQuoteArgs(cmd)).wait(), 0)
100
101   qa_config.ReleaseInstance(instance)
102
103
104 @qa_utils.DefineHook('instance-startup')
105 def TestInstanceStartup(instance):
106   """gnt-instance startup"""
107   master = qa_config.GetMasterNode()
108
109   cmd = ['gnt-instance', 'startup', instance['name']]
110   AssertEqual(StartSSH(master['primary'],
111                        utils.ShellQuoteArgs(cmd)).wait(), 0)
112
113
114 @qa_utils.DefineHook('instance-shutdown')
115 def TestInstanceShutdown(instance):
116   """gnt-instance shutdown"""
117   master = qa_config.GetMasterNode()
118
119   cmd = ['gnt-instance', 'shutdown', instance['name']]
120   AssertEqual(StartSSH(master['primary'],
121                        utils.ShellQuoteArgs(cmd)).wait(), 0)
122
123
124 @qa_utils.DefineHook('instance-reinstall')
125 def TestInstanceReinstall(instance):
126   """gnt-instance reinstall"""
127   master = qa_config.GetMasterNode()
128
129   cmd = ['gnt-instance', 'reinstall', '-f', instance['name']]
130   AssertEqual(StartSSH(master['primary'],
131                        utils.ShellQuoteArgs(cmd)).wait(), 0)
132
133
134 @qa_utils.DefineHook('instance-failover')
135 def TestInstanceFailover(instance):
136   """gnt-instance failover"""
137   master = qa_config.GetMasterNode()
138
139   cmd = ['gnt-instance', 'failover', '--force', instance['name']]
140   AssertEqual(StartSSH(master['primary'],
141                        utils.ShellQuoteArgs(cmd)).wait(), 0)
142
143
144 @qa_utils.DefineHook('instance-info')
145 def TestInstanceInfo(instance):
146   """gnt-instance info"""
147   master = qa_config.GetMasterNode()
148
149   cmd = ['gnt-instance', 'info', instance['name']]
150   AssertEqual(StartSSH(master['primary'],
151                        utils.ShellQuoteArgs(cmd)).wait(), 0)
152
153
154 @qa_utils.DefineHook('instance-modify')
155 def TestInstanceModify(instance):
156   """gnt-instance modify"""
157   master = qa_config.GetMasterNode()
158
159   orig_memory = qa_config.get('mem')
160   orig_bridge = qa_config.get('bridge', 'xen-br0')
161   args = [
162     ["--memory", "128"],
163     ["--memory", str(orig_memory)],
164     ["--cpu", "2"],
165     ["--cpu", "1"],
166     ["--bridge", "xen-br1"],
167     ["--bridge", orig_bridge],
168     ["--kernel", "/dev/null"],
169     ["--kernel", "default"],
170     ["--initrd", "/dev/null"],
171     ["--initrd", "none"],
172     ["--initrd", "default"],
173     ["--hvm-boot-order", "acn"],
174     ["--hvm-boot-order", "default"],
175     ]
176   for alist in args:
177     cmd = ['gnt-instance', 'modify'] + alist + [instance['name']]
178     AssertEqual(StartSSH(master['primary'],
179                          utils.ShellQuoteArgs(cmd)).wait(), 0)
180
181   # check no-modify
182   cmd = ['gnt-instance', 'modify', instance['name']]
183   AssertNotEqual(StartSSH(master['primary'],
184                           utils.ShellQuoteArgs(cmd)).wait(), 0)
185
186
187 @qa_utils.DefineHook('instance-list')
188 def TestInstanceList():
189   """gnt-instance list"""
190   master = qa_config.GetMasterNode()
191
192   cmd = ['gnt-instance', 'list']
193   AssertEqual(StartSSH(master['primary'],
194                        utils.ShellQuoteArgs(cmd)).wait(), 0)
195
196
197 @qa_utils.DefineHook('backup-export')
198 def TestInstanceExport(instance, node):
199   """gnt-backup export"""
200   master = qa_config.GetMasterNode()
201
202   cmd = ['gnt-backup', 'export', '-n', node['primary'], instance['name']]
203   AssertEqual(StartSSH(master['primary'],
204                        utils.ShellQuoteArgs(cmd)).wait(), 0)
205
206   return qa_utils.ResolveInstanceName(instance)
207
208
209 @qa_utils.DefineHook('backup-import')
210 def TestInstanceImport(node, newinst, expnode, name):
211   """gnt-backup import"""
212   master = qa_config.GetMasterNode()
213
214   cmd = (['gnt-backup', 'import',
215           '--disk-template=plain',
216           '--no-ip-check',
217           '--src-node=%s' % expnode['primary'],
218           '--src-dir=%s/%s' % (constants.EXPORT_DIR, name),
219           '--node=%s' % node['primary']] +
220          _GetGenericAddParameters())
221   cmd.append(newinst['name'])
222   AssertEqual(StartSSH(master['primary'],
223                        utils.ShellQuoteArgs(cmd)).wait(), 0)
224
225
226 @qa_utils.DefineHook('backup-list')
227 def TestBackupList(expnode):
228   """gnt-backup list"""
229   master = qa_config.GetMasterNode()
230
231   cmd = ['gnt-backup', 'list', '--node=%s' % expnode['primary']]
232   AssertEqual(StartSSH(master['primary'],
233                        utils.ShellQuoteArgs(cmd)).wait(), 0)
234
235
236 def _TestInstanceDiskFailure(instance, node, node2, onmaster):
237   """Testing disk failure."""
238   master = qa_config.GetMasterNode()
239   sq = utils.ShellQuoteArgs
240
241   instance_full = qa_utils.ResolveInstanceName(instance)
242   node_full = qa_utils.ResolveNodeName(node)
243   node2_full = qa_utils.ResolveNodeName(node2)
244
245   cmd = ['gnt-node', 'volumes', '--separator=|', '--no-headers',
246          '--output=node,phys,instance',
247          node['primary'], node2['primary']]
248   output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
249
250   # Get physical disk names
251   re_disk = re.compile(r'^/dev/([a-z]+)\d+$')
252   node2disk = {}
253   for line in output.splitlines():
254     (node_name, phys, inst) = line.split('|')
255     if inst == instance_full:
256       if node_name not in node2disk:
257         node2disk[node_name] = []
258
259       m = re_disk.match(phys)
260       if not m:
261         raise qa_error.Error("Unknown disk name format: %s" % disk)
262
263       name = m.group(1)
264       if name not in node2disk[node_name]:
265         node2disk[node_name].append(name)
266
267   if [node2_full, node_full][int(onmaster)] not in node2disk:
268     raise qa_error.Error("Couldn't find physical disks used on"
269                          " %s node" % ["secondary", "master"][int(onmaster)])
270
271   # Check whether nodes have ability to stop disks
272   for node_name, disks in node2disk.iteritems():
273     cmds = []
274     for disk in disks:
275       cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
276     AssertEqual(StartSSH(node_name, ' && '.join(cmds)).wait(), 0)
277
278   # Get device paths
279   cmd = ['gnt-instance', 'activate-disks', instance['name']]
280   output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
281   devpath = []
282   for line in output.splitlines():
283     (_, _, tmpdevpath) = line.split(':')
284     devpath.append(tmpdevpath)
285
286   # Get drbd device paths
287   cmd = ['gnt-instance', 'info', instance['name']]
288   output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
289   pattern = (r'\s+-\s+type:\s+drbd,\s+.*$'
290              r'\s+primary:\s+(/dev/drbd\d+)\s+')
291   drbddevs = re.findall(pattern, output, re.M)
292
293   halted_disks = []
294   try:
295     # Deactivate disks
296     cmds = []
297     for name in node2disk[[node2_full, node_full][int(onmaster)]]:
298       halted_disks.append(name)
299       cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
300     AssertEqual(StartSSH([node2, node][int(onmaster)]['primary'],
301                          ' && '.join(cmds)).wait(), 0)
302
303     # Write something to the disks and give some time to notice the problem
304     cmds = []
305     for disk in devpath:
306       cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
307                       "if=%s" % disk, "of=%s" % disk]))
308     for _ in (0, 1, 2):
309       AssertEqual(StartSSH(node['primary'], ' && '.join(cmds)).wait(), 0)
310       time.sleep(3)
311
312     for name in drbddevs:
313       cmd = ['drbdsetup', name, 'show']
314       AssertEqual(StartSSH(node['primary'], sq(cmd)).wait(), 0)
315
316     # For manual checks
317     cmd = ['gnt-instance', 'info', instance['name']]
318     AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
319
320   finally:
321     # Activate disks again
322     cmds = []
323     for name in halted_disks:
324       cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
325     AssertEqual(StartSSH([node2, node][int(onmaster)]['primary'],
326                          '; '.join(cmds)).wait(), 0)
327
328   if onmaster:
329     for name in drbddevs:
330       cmd = ['drbdsetup', name, 'detach']
331       AssertEqual(StartSSH(node['primary'], sq(cmd)).wait(), 0)
332   else:
333     for name in drbddevs:
334       cmd = ['drbdsetup', name, 'disconnect']
335       AssertEqual(StartSSH(node2['primary'], sq(cmd)).wait(), 0)
336
337   # Make sure disks are up again
338   #cmd = ['gnt-instance', 'activate-disks', instance['name']]
339   #AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
340
341   # Restart instance
342   cmd = ['gnt-instance', 'shutdown', instance['name']]
343   AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
344
345   #cmd = ['gnt-instance', 'startup', '--force', instance['name']]
346   cmd = ['gnt-instance', 'startup', instance['name']]
347   AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
348
349   cmd = ['gnt-cluster', 'verify']
350   AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
351
352
353 def TestInstanceMasterDiskFailure(instance, node, node2):
354   """Testing disk failure on master node."""
355   print qa_utils.FormatError("Disk failure on primary node cannot be"
356                              " tested due to potential crashes.")
357   # The following can cause crashes, thus it's disabled until fixed
358   #return _TestInstanceDiskFailure(instance, node, node2, True)
359
360
361 def TestInstanceSecondaryDiskFailure(instance, node, node2):
362   """Testing disk failure on secondary node."""
363   return _TestInstanceDiskFailure(instance, node, node2, False)