Fix --node option for “gnt-backup list”.
[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-remove')
86 def TestInstanceRemove(instance):
87   """gnt-instance remove"""
88   master = qa_config.GetMasterNode()
89
90   cmd = ['gnt-instance', 'remove', '-f', instance['name']]
91   AssertEqual(StartSSH(master['primary'],
92                        utils.ShellQuoteArgs(cmd)).wait(), 0)
93
94   qa_config.ReleaseInstance(instance)
95
96
97 @qa_utils.DefineHook('instance-startup')
98 def TestInstanceStartup(instance):
99   """gnt-instance startup"""
100   master = qa_config.GetMasterNode()
101
102   cmd = ['gnt-instance', 'startup', instance['name']]
103   AssertEqual(StartSSH(master['primary'],
104                        utils.ShellQuoteArgs(cmd)).wait(), 0)
105
106
107 @qa_utils.DefineHook('instance-shutdown')
108 def TestInstanceShutdown(instance):
109   """gnt-instance shutdown"""
110   master = qa_config.GetMasterNode()
111
112   cmd = ['gnt-instance', 'shutdown', instance['name']]
113   AssertEqual(StartSSH(master['primary'],
114                        utils.ShellQuoteArgs(cmd)).wait(), 0)
115
116
117 @qa_utils.DefineHook('instance-reinstall')
118 def TestInstanceReinstall(instance):
119   """gnt-instance reinstall"""
120   master = qa_config.GetMasterNode()
121
122   cmd = ['gnt-instance', 'reinstall', '-f', instance['name']]
123   AssertEqual(StartSSH(master['primary'],
124                        utils.ShellQuoteArgs(cmd)).wait(), 0)
125
126
127 @qa_utils.DefineHook('instance-failover')
128 def TestInstanceFailover(instance):
129   """gnt-instance failover"""
130   master = qa_config.GetMasterNode()
131
132   cmd = ['gnt-instance', 'failover', '--force', instance['name']]
133   AssertEqual(StartSSH(master['primary'],
134                        utils.ShellQuoteArgs(cmd)).wait(), 0)
135
136
137 @qa_utils.DefineHook('instance-info')
138 def TestInstanceInfo(instance):
139   """gnt-instance info"""
140   master = qa_config.GetMasterNode()
141
142   cmd = ['gnt-instance', 'info', instance['name']]
143   AssertEqual(StartSSH(master['primary'],
144                        utils.ShellQuoteArgs(cmd)).wait(), 0)
145
146
147 @qa_utils.DefineHook('instance-list')
148 def TestInstanceList():
149   """gnt-instance list"""
150   master = qa_config.GetMasterNode()
151
152   cmd = ['gnt-instance', 'list']
153   AssertEqual(StartSSH(master['primary'],
154                        utils.ShellQuoteArgs(cmd)).wait(), 0)
155
156
157 @qa_utils.DefineHook('backup-export')
158 def TestInstanceExport(instance, node):
159   """gnt-backup export"""
160   master = qa_config.GetMasterNode()
161
162   cmd = ['gnt-backup', 'export', '-n', node['primary'], instance['name']]
163   AssertEqual(StartSSH(master['primary'],
164                        utils.ShellQuoteArgs(cmd)).wait(), 0)
165
166   return qa_utils.ResolveInstanceName(instance)
167
168
169 @qa_utils.DefineHook('backup-import')
170 def TestInstanceImport(node, newinst, expnode, name):
171   """gnt-backup import"""
172   master = qa_config.GetMasterNode()
173
174   cmd = (['gnt-backup', 'import',
175           '--disk-template=plain',
176           '--no-ip-check',
177           '--src-node=%s' % expnode['primary'],
178           '--src-dir=%s/%s' % (constants.EXPORT_DIR, name),
179           '--node=%s' % node['primary']] +
180          _GetGenericAddParameters())
181   cmd.append(newinst['name'])
182   AssertEqual(StartSSH(master['primary'],
183                        utils.ShellQuoteArgs(cmd)).wait(), 0)
184
185
186 @qa_utils.DefineHook('backup-list')
187 def TestBackupList(expnode):
188   """gnt-backup list"""
189   master = qa_config.GetMasterNode()
190
191   cmd = ['gnt-backup', 'list', '--node=%s' % expnode['primary']]
192   AssertEqual(StartSSH(master['primary'],
193                        utils.ShellQuoteArgs(cmd)).wait(), 0)
194
195
196 def _TestInstanceDiskFailure(instance, node, node2, onmaster):
197   """Testing disk failure."""
198   master = qa_config.GetMasterNode()
199   sq = utils.ShellQuoteArgs
200
201   instance_full = qa_utils.ResolveInstanceName(instance)
202   node_full = qa_utils.ResolveNodeName(node)
203   node2_full = qa_utils.ResolveNodeName(node2)
204
205   cmd = ['gnt-node', 'volumes', '--separator=|', '--no-headers',
206          '--output=node,phys,instance',
207          node['primary'], node2['primary']]
208   output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
209
210   # Get physical disk names
211   re_disk = re.compile(r'^/dev/([a-z]+)\d+$')
212   node2disk = {}
213   for line in output.splitlines():
214     (node_name, phys, inst) = line.split('|')
215     if inst == instance_full:
216       if node_name not in node2disk:
217         node2disk[node_name] = []
218
219       m = re_disk.match(phys)
220       if not m:
221         raise qa_error.Error("Unknown disk name format: %s" % disk)
222
223       name = m.group(1)
224       if name not in node2disk[node_name]:
225         node2disk[node_name].append(name)
226
227   if [node2_full, node_full][int(onmaster)] not in node2disk:
228     raise qa_error.Error("Couldn't find physical disks used on "
229                          "%s node" % ["secondary", "master"][int(onmaster)])
230
231   # Check whether nodes have ability to stop disks
232   for node_name, disks in node2disk.iteritems():
233     cmds = []
234     for disk in disks:
235       cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
236     AssertEqual(StartSSH(node_name, ' && '.join(cmds)).wait(), 0)
237
238   # Get device paths
239   cmd = ['gnt-instance', 'activate-disks', instance['name']]
240   output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
241   devpath = []
242   for line in output.splitlines():
243     (_, _, tmpdevpath) = line.split(':')
244     devpath.append(tmpdevpath)
245
246   # Get drbd device paths
247   cmd = ['gnt-instance', 'info', instance['name']]
248   output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
249   pattern = (r'\s+-\s+type:\s+drbd,\s+.*$'
250              r'\s+primary:\s+(/dev/drbd\d+)\s+')
251   drbddevs = re.findall(pattern, output, re.M)
252
253   halted_disks = []
254   try:
255     # Deactivate disks
256     cmds = []
257     for name in node2disk[[node2_full, node_full][int(onmaster)]]:
258       halted_disks.append(name)
259       cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
260     AssertEqual(StartSSH([node2, node][int(onmaster)]['primary'],
261                          ' && '.join(cmds)).wait(), 0)
262
263     # Write something to the disks and give some time to notice the problem
264     cmds = []
265     for disk in devpath:
266       cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
267                       "if=%s" % disk, "of=%s" % disk]))
268     for _ in (0, 1, 2):
269       AssertEqual(StartSSH(node['primary'], ' && '.join(cmds)).wait(), 0)
270       time.sleep(3)
271
272     for name in drbddevs:
273       cmd = ['drbdsetup', name, 'show']
274       AssertEqual(StartSSH(node['primary'], sq(cmd)).wait(), 0)
275
276     # For manual checks
277     cmd = ['gnt-instance', 'info', instance['name']]
278     AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
279
280   finally:
281     # Activate disks again
282     cmds = []
283     for name in halted_disks:
284       cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
285     AssertEqual(StartSSH([node2, node][int(onmaster)]['primary'],
286                          '; '.join(cmds)).wait(), 0)
287
288   if onmaster:
289     for name in drbddevs:
290       cmd = ['drbdsetup', name, 'detach']
291       AssertEqual(StartSSH(node['primary'], sq(cmd)).wait(), 0)
292   else:
293     for name in drbddevs:
294       cmd = ['drbdsetup', name, 'disconnect']
295       AssertEqual(StartSSH(node2['primary'], sq(cmd)).wait(), 0)
296
297   # Make sure disks are up again
298   #cmd = ['gnt-instance', 'activate-disks', instance['name']]
299   #AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
300
301   # Restart instance
302   cmd = ['gnt-instance', 'shutdown', instance['name']]
303   AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
304
305   #cmd = ['gnt-instance', 'startup', '--force', instance['name']]
306   cmd = ['gnt-instance', 'startup', instance['name']]
307   AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
308
309   cmd = ['gnt-cluster', 'verify']
310   AssertEqual(StartSSH(master['primary'], sq(cmd)).wait(), 0)
311
312
313 def TestInstanceMasterDiskFailure(instance, node, node2):
314   """Testing disk failure on master node."""
315   print qa_utils.FormatError("Disk failure on primary node cannot be "
316                              "tested due to potential crashes.")
317   # The following can cause crashes, thus it's disabled until fixed
318   #return _TestInstanceDiskFailure(instance, node, node2, True)
319
320
321 def TestInstanceSecondaryDiskFailure(instance, node, node2):
322   """Testing disk failure on secondary node."""
323   return _TestInstanceDiskFailure(instance, node, node2, False)